3.友元类
1.友元类的引入
如果想要在一个类中访问另一个类的成员,就需要将这个类声明为另一个类的友元类。
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
2.友元类的说明
- 友元关系是单向的,不具有交换性;
比如上述Time类
和Date类
,在Time类
中声明Date类
为其友元类,那么可以在Date类
中直接访问Time类
的私有成员变量,但想在Time类
中访问Date类
中私有的成员变量则不行。 - 友元关系不能传递;
如果C是B的友元, B是A的友元,则不能说明C时A的友元。 - 友元关系不能继承。
class Time { friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量 public: Time(int hour = 0, int minute = 0, int second = 0) : _hour(hour) , _minute(minute) , _second(second) {} private: int _hour; int _minute; int _second; }; class Date { public: Date(int year = 1900, int month = 1, int day = 1) : _year(year) , _month(month) , _day(day) {} void SetTimeOfDate(int hour, int minute, int second) { // 直接访问时间类私有的成员变量 _t._hour = hour; _t._minute = minute; _t._second = second; } private: int _year; int _month; int _day; Time _t; };
四、内部类
1.概念
如果一个类定义在另一个类里面。则这个类就叫做另一个类的内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问它,外部类对内部类没有任何特殊的访问权限(即,和其他类或对象的访问限制没有区别)。
但是,内部类天生就是外部类的友元类。友元类的概念参照上文,内部类可以通过外部类的对象参数访问外部类的所有成员,但是外部类不是内部类的友元。
2.特性
- 内部类可以定义在外部类的
public
、protect
、private
的限制中; - 内部类可以直接访问外部类的static成员,而不需要借助外部类的类名或者外部类的对象;
sizeof(外部类)=外部类
,和内部类没有关系。
观察下面代码:
class A { private: static int k; int h = 2; public: class B // B天生就是A的友元,但是A不是B的友元 { public: void func(const A& a) { cout << k << endl;//可以访问 cout << a.h << endl;//可以访问 } }; }; int A::k = 1; int main() { A::B b; b.func(A()); return 0; }
运行结果:
五、匿名对象
一般情况下,我们定义一个对象都会给它一个对应的一个名字。特殊的,我们可以定义一个没有名字的对象——匿名对象。
这种对象的生命周期只有它定义所在的那一行,运行到下一行就会销毁,属于一次性的对象,所以不需要命名。
观察以下代码:
class A { public: A(int a = 0) :_a(a) { cout << "A(int a)" << endl; } ~A() { cout << "~A()" << endl; } private: int _a; }; class Solution { public: int Sum_Solution(int n) { //... return n; } }; int main() { A aa1; // 不能像下面这一行这样定义对象,因为编译器无法识别它是一个函数声明,还是对象定义 //A aa1(); // 我们可以像下一行这样定义匿名对象,匿名对象的特点是不用取名字,但是他的生命周期只有这一行,到下一行他就会自动调用析构函数 A(); A aa2(2); // 匿名对象在这样场景下就会好用(只需要使用一次,就可以进行销毁) Solution().Sum_Solution(10); return 0; }
六、拷贝构造对象时编译器的优化
上文中介绍了一种拷贝构造对象时编译器的优化,接下来我们了解还有哪些拷贝构造对象时优化。
在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的。
一般编译器会在不影响程序结果的情况下对程序进行一些优化,观察下面代码,了解这几种可以优化的情况:
class A { public: A(int a = 0) :_a(a) { cout << "A(int a)" << endl; } A(const A& aa) :_a(aa._a) { cout << "A(const A& aa)" << endl; } A& operator=(const A& aa) { cout << "A& operator=(const A& aa)" << endl; if (this != &aa) { _a = aa._a; } return *this; } ~A() { cout << "~A()" << endl; } private: int _a; }; void f1(A aa) {} A f2() { A aa; return aa; } int main() { // 传值传参 A aa1; f1(aa1); cout << endl; // 传值返回 f2(); cout << endl; // 隐式类型,连续构造+拷贝构造->优化为直接构造 f1(1); // 一个表达式中,连续构造+拷贝构造->优化为一个构造 f1(A(2)); cout << endl; // 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造 A aa2 = f2(); cout << endl; // 一个表达式中,连续拷贝构造+赋值重载->无法优化 aa1 = f2(); cout << endl; return 0; }
七、再次理解类和对象
类是对某一类实体(对象)来进行描述的,描述该对象具有那
些属性,那些方法,描述完成后就形成了一种新的自定义类型,采用该自定义类型就可以实例化出具体的对象。
总结
以上就是今天要讲的内容,本文介绍了构造函数中的初始化列表、隐式类型转换、类的静态成员、友元、内部类、匿名对象以及编译器对拷贝构造的优化等相关概念。本文作者目前也是正在学习C++相关的知识,如果文章中的内容有错误或者不严谨的部分,欢迎大家在评论区指出,也欢迎大家在评论区提问、交流。
最后,如果本篇文章对你有所启发的话,希望可以多多支持作者,谢谢大家!