当前位置: 首页 > news >正文

C++深入类与对象

在上一篇中提到了构造函数,那么这篇再来提一下构造函数,编译器自动生成的默认构造函数对于内置类型不做处理,自定义类型会调用它自己的构造函数。对于自己写的构造函数,之前是在函数体中初始化,当然不止这一种初始化,还有初始化列表的方式进行初始化,那么为什么可以在函数体中初始化还需要初始化列表呢?咱接着往下看!

目录

构造函数:初始化列表

explicit关键字 

Static成员 

友元 

友元函数

 友元类

内部类 


构造函数:初始化列表

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括 号中的初始值或表达式。

class Date{public:Date(int year, int month, int day): _year(year), _month(month),_day(day){}private:int _year;int _month;int _day;};

以日期类来看,在之前是在函数体中对对象的成员进行赋值,而初始化列表和其有一样的作用,当然只看初始化内置类型肯定是不行,这样觉得和在函数体中初始化没什么区别,那么如果是对自定义类型和const修饰类型和引用呢,在函数体中还能初始化吗?

对于自定义类型,初始化列表时可以自己写也可以不写,因为编译器会自动调用自定义类型自己的构造函数进行初始化。const修饰的变量和引用在定义时就需要初始化,而如果是在函数体中去初始化是会报错的,所以只能使用初始化列表来对其初始化。

示例:

class A
{
public:A(int a):_a(1){cout << "A" << endl;}
private:int _a;
};class B {
public:B(int a, int& ref):_ref(ref),_n(a),_ab(a){}
private:A _ab;//特征: 必须在定义的时候初始化int& _ref;const int _n;
};
int main()
{int n = 10;B a(10, n);return 0;
}

在这段代码中有自定义类型,引用,const成员变量三个成员变量, 那么我们看一下是否对其初始化了:

 通过调试可以看出是我们想要的结果。

需要注意的是:

1. 每个成员变量在初始化列表中最多只能出现一次(初始化只能初始化一次)

2. 类中包含以下成员,必须放在初始化列表位置进行初始化:

引用成员变量

const成员变量

自定义类型成员(且该类没有默认构造函数时)

3、尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使 用初始化列表初始化。

4、成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

 示例:

class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() {cout<<_a1<<" "<<_a2<<endl;}
private:int _a2;int _a1;
};int main() {A aa(1);aa.Print();
}

各位可以看看输出结果是什么? 先说答案 输出结果为1 和随机值 ,那么为什么是1和随机值呢?

这就和初始化的顺序有关了,初始化列表初始化的顺序和初始化列表中的顺序无关,而是和声明的顺序有关 ,在这题中,_a2先声明,所以初始化时先对_a2初始化,但是在初始化列表中把_a1 的值给了_a2,所以_a2为随机值,在对_a1初始化,所以_a1的值为1。

运行结果:

explicit关键字 

构造函数不仅可以构造与初始化对象,对于接收单个参数的构造函数,还具有类型转换的作用。接收单个参 数的构造函数具体表现:

1. 构造函数只有一个参数

2. 构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值

3. 全缺省构造函数

示例:

class A{public:/*explicit A(int a):_a(a){cout << "A(int a)" << endl;}*/A(int a):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}private:int _a;};int main(){A aa1(1);A aa2 = 2;return 0;}

在没有explicit修饰的情况下,A aa2 = 2 会进行隐式类型转换,整型转换为自定义类型,2构造一个A的临时对象(临时对象具有常性),临时对象再拷贝构造aa2 -->优化用2直接构造。

在explicit修饰的情况下,以上转换就不能进行。

运行结果:

Static成员 

概念:声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的 成员函数,称之为静态成员函数静态成员变量一定要在类外进行初始化。

 示例:

class A
{
public:A() { ++_scount; }A(const A& t) { ++_scount; }~A() { --_scount; }static int GetACount() { return _scount; }
private:static int _scount;
};int A::_scount = 0;void TestA()
{cout << A::GetACount() << endl;A a1, a2;A a3(a1);cout << A::GetACount() << endl;
}

静态成员变量需要在类外面定义,想要不通过对象来访问成员函数可以用static来修饰成员函数,就可以访问该成员函数。 

特性:

1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区

2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明

3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问

4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员

5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制 

友元 

友元函数

在实现日期类时,对于输入输出日期不能简单的使用流提取和流插入,而是需要重载该两个运算符,因为正常的流提取和流插入达不到预期效果。cout的输出流对 象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用 中cout需要是第一个形参对象,才能正常使用。所以要将operator重载成全局函数。但又会导致类外没办 法访问成员,此时就需要友元来解决。

示例:

class Date
{friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}istream& operator>>(istream& _cin, Date& d)
{_cin >> d._year;_cin >> d._month;_cin >> d._day;return _cin;
}int main()
{Date d;cin >> d;cout << d << endl;return 0;
}

上述代码对<< 和>> 重载来实现日期类的输入输出,在类中声明两个函数为友元函数,便可以访问类的私有成员变量 。

注意:

友元函数可访问类的私有和保护成员,但不是类的成员函数

友元函数不能用const修饰

友元函数可以在类定义的任何地方声明,不受类访问限定符限制

一个函数可以是多个类的友元函数

友元函数的调用与普通函数的调用原理相同

 友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

友元关系是单向的,不具有交换性。 友元关系不能传递 如果B是A的友元,C是B的友元,则不能说明C时A的友元。

友元类的声明和友元函数的声明一样,加friend修饰。

内部类 

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外 部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。

内部类是外部类的天生友元,内部类可以访问外部类的私有变量,但是外部类不可以访问内部类的私有变量。

特性:

1. 内部类可以定义在外部类的public、protected、private都是可以的。

2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。

3. sizeof(外部类)=外部类,和内部类没有任何关系。

示例:

class A {
public:class B {public:void Fun(const A& a){cout << a._a << endl;}};
private:int _a;
};int main()
{A::B bb;bb.Fun(A());
}

相关文章:

  • Spring Boot启动慢?Redis缓存击穿?Kafka消费堆积?——Java后端常见问题排查实战
  • 无公网ip远程桌面连接不了怎么办?内网计算机让外网访问方法和问题分析
  • MCP Python技术实践
  • Spring Data Redis 实战指南
  • 【基于SpringBoot的图书购买系统】Redis中的数据以分页的形式展示:从配置到前后端交互的完整实现
  • 【手搓一个原生全局loading组件解决页面闪烁问题】
  • python打卡训练营打卡记录day41
  • 机器学习知识图谱——K-means++聚类算法
  • AnyTXT Searcher 文档内容搜索工具 v1.3.2034 官方版
  • MySQL--day10--数据处理之增删改
  • 《管理经济》期末复习题(2)
  • 智能测试新范式:GenAI 与 Playwright MCP 如何重塑 QA 流程
  • 学术合作交流
  • Dest建筑能耗模拟仿真功能简介
  • InfluxQL 数据分析实战:聚合、过滤与关联查询全解析
  • 【Linux】mmap文件内存映射
  • QuickJS 在生物化学计算中的应用
  • 5.1 初探大数据流式处理
  • 云原生安全基石:Kubernetes 核心概念与安全实践指南
  • 【harbor】--基础使用
  • 有关网站建设的文章/外贸网站免费推广b2b
  • 毕节城乡建设局网站/百度统计流量研究院
  • 天津网站策划/郑州搜狗关键词优化顾问
  • 瑞华特散热器网站谁给做的/济南seo网站排名关键词优化
  • 网站运营岗位介绍/谷歌广告推广怎么做
  • WordPress 代码建站/新闻热点事件2021(最新)