四、友元类和友元函数
在类和对象中,我们就已经接触过了,友元函数作用就是,偷家!!
class Date { friend inline ostream& operator<<(ostream& out, Date& d) ... private: int _year; int _month; int _day; }; inline ostream& operator<<(ostream& out, Date& d) { out << d._year << " " << d._month << " " << d._day << endl; return out; }
当定义在类外的函数要使用私有变量时,就可以通过友元函数来访问。友元函数它就是一个普通函数,他没有this指针。
友元类也是偷家,只不过这次换成了类和类直接:
class A { friend Date B;//友元类 private: int _a; static int k; public: ... }; class B { private: int _c; int k; public: ... };
B是A的友元类,B能访问A的私有成员变量,但A不能访问B的私有成员变量。(假朋友)
五、内部类(C++中不重要)
就是类套类(类种类)
class A { private: int _a; static int k; public: // B天生就是A的友元 class B { int _b; void foo(const A& a) { cout << k << endl;//OK,可以访问 cout << a._a << endl;//OK } }; };
1.计算类的内存大小时,sizeof(A)答案是四,因为A对象里没有B,只有自己的成员,这里就可以看作:他们两仅仅是嵌套定义,相当于两个独立的类。
但B类的访问受A类域访问限定符的限制!
A aa; //aa中没有B的对象
如果要创建一个B的对象,需要 A:: B bb;
只是域限定关系!
2.类种类,被套在里面的类天生是外面类的友元类,B可以访问A,但A不能访问B。
所以定义类时,如果有内部类,你就要小心了,小心不注意把你家偷光!
六、匿名对象
创建类有几种方式呢?
class A { public: A(int a=0) :N(a) {} int Sum_Solution() { return N; } private: int N; }; int main() { // 有名对象 A aa0; A aa1(1); A aa2 = 2; //单参数创建类隐式类型转化 //A aa3(); //不ok,会与函数声明冲突 // 匿名对象 --生命周期当前这一行 A(); A(3); //A so; //A.Sum_Solution(10); A().Sum_Solution(10); return 0; }
匿名对象不需要对象名,且生命周期只是当前这一行。
当我们只是为了访问成员函数,而创建类去访问,那可不可以简单一些呢?
A so; A.Sum_Solution(10);
A().Sum_Solution(10); 这两个是一样的,这一行结束,匿名对象会自动调用析构函数。
七、编译器中的一些优化
编译器这些优化,只存在于 构造函数和拷贝构造函数之间,且适合一个表达式中的连续步骤,优化的前提当然不能改变原本的正确性!
下面来看几个例子,来了解如何优化,怎么优化:
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; } A f3() { /*A aa(10); return aa;*/ return A(10); } int main() { // 优化场景1 A aa1 = 1; // A tmp(1) + A aa1(tmp) -> 优化 A aa1(1) // 优化场景2 A aa1(1); f1(aa1); -------------- f1(A(1)); // 构造 + 拷贝构造 -> 优化 构造 f1(1); // 构造 + 拷贝构造 -> 优化 构造 //优化场景3 f2(); // 构造+拷贝构造 A ret = f2(); // 构造+拷贝构造+拷贝构造 ->优化 构造+拷贝构造,中间可能对aa还有一些操作, // 不是连续的一个表达式 ,所以无法直接优化为一个构造 //优化场景4 A ret; ret = f2(); ---------------- A ret = f3(); // 构造+拷贝构造+拷贝构造 -> 优化 -> 构造 return 0; }
场景1.
前面的单参数隐式类型创建类:
先拿1构造一个临时变量,再用临时变量拷贝aa1(构造+拷贝构造)
优化方式:直接为:A aa1(1);就是构造函数
场景2.
A aa1(1); f1(aa1); 定义一个类,传参调用(构造+拷贝构造),无法优化,不保证是否需要对aa1对象进行操作,所以优化都是一个表达式中的连续步骤。
f1(A(1)); 直接利用匿名函数创建类,传值返回。(构造 + 拷贝构造 -> 优化 :构造),因为匿名对象创建完自动析构,就相当于直接拿1去构造了f(1)中的形参 aa了
f1(1); 单参数隐式类型创建对象,1去构造临时变量,临时变量去拷贝形参。( 构造 + 拷贝构造 -> 优化 :构造)
场景3:
f(2)中,先构造一个类,再传值返回,就需要拷贝到临时变量。(构造+拷贝)
A ret = f2();先构造,再拷贝,再拷贝,因为要把返回值拷贝构造到ret对象。(构造+拷贝+拷贝),优化:在拷贝临时变量时,就直接将临时变量当作ret对象拷贝构造了。省略了第三步(构造+拷贝)。
场景4:
A ret; ret = f2();事先创建好了ret对象,ret = f2();不是拷贝构造,是赋值。(构造+拷贝)
A ret = f3();都在一个步骤里。 f3();中创建的是匿名对象,一个构造,后面还是一样,拷贝+拷贝。(构造+拷贝+拷贝)。优化:直接是一个构造函数。
f(3);中,直接创建一个匿名对象,不担心对他有其他操作,就是一个表达式中连续的步骤,直接优化,不会出现错误。
总结:
类和对象到现在就告一段落了,但是在日后的学习我们还是需要不断地回顾,毕竟知识是连续,联系性比较强的,大家继续加油!后面再见!