友元
friend关键字修饰函数,使得这个函数是可以突破类的封装,但是友元会增加耦合度(关联程度),破环了封装,所以友元不能多用
分为:友元函数和友元类
友元函数就是在一个类中对于成员函数加上friend修饰,在类外进行定义
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
因为在类中成员函数会使得this指针占据第一个位置,对于某些功能如重载<<需要cin在第一个参数位置(与this就会争夺,但是this默认为第一个位置的形参),所以我们考虑到可以不使用成员函数,将重载<<放在全局域,但是这个时候因为private限制导致类外无法访问自定义类型,所以我们推出来友元这个定义,实际上就是一个普通函数,只是在类中声明的普通函数(没有this指针),但是因为在类中所以可以访问私有成员变量
注意:
- 友元函数可访问类的私有和保护成员,但不是类的成员函数
- 友元函数不能用const修饰
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
- 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数的调用原理相同
友元类
友元类实际上就是在另一个类中声明的一个类,使得这个友元类的所有(成员变量or成员函数)都可以访问该类的私有成员
//下面是对于友元类的例子 #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; class A { public: int get() { return d._year; } private: Date d;//创建Date类中的对象d ,然后就可以通过d来调用Date的私有成员变量了 }; class Date { friend class A;//声明A为Date的友元类 public: Date(int year, int month, int day) :_year(year) , _month(month) , _day(day) {} //重载>>运算符 friend ostream& operator>>(ostream& out, Date& d); //{ // //} private: int _year; int _month; int _day; }; ostream& operator>>(ostream& out, Date& d) { out << d._year << "-" << d._month << "-" << d._day << endl; } int main() { return 0; }
注意:
- 友元关系的单向的,不具有交换性,如上述Date不能访问A的私有成员,但是A可以访问Date的私有成员
- 友元关系是不能传递的
- 友元关系不能继承,继承的话属于父子类,这个概念后序再讲
实际上友元类,在其中声明Date类成员,就是因为当我们需要使用这个Date类中的成员变量时候,通过friend访问访问私有or保护的成员变量,从而实现类外访问
内部类
内部类:如果一个类定义在另一个类的内部,就叫做内部类,内部类是独立的个体,不属于外部类,也并不能通过外部类的对象去访问内部类的成员,但是内部类是天生的友元类(外部类的友元)
特征:
内部类可以放在外部类的任何地方,不受访问限制符的限制
内部类可以直接访问外部类static成员,不需要外部类的类名or对象
sizeof(外部类)=外部类,内部类不影响外部类的大小
//例子 class A { friend class C; public: void get() {} class B//内部类 { public: void foo(const A& a) { cout << k << endl;//OK //可以直接访问静态变量不需要类名or对象 cout << a.h << endl;//OK } }; private: int h; static int k; }; class C//友元类 { public: void get(C& c) { a.h; a.k; } int date; A a; }; int A::k = 0; int main() { return 0; }
匿名对象
匿名对象的使用对于某些场景的情况下是比较好用的,匿名对象就是直接用类名加上(),如:A(),来使用的,创建这个对象之后,再下一行代码就会析构,所以匿名对象由此得名(不会留下痕迹)
//匿名对象的用法 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; }; int main() { //正常实例化对象 A a(1);//1调用构造函数,实例化对象a //当只有无参构造的时候(or默认构造函数) 实例化对象应该为 A a;//这样即可 不能加上() //A a(); 这个代码是错误的,因为编译器无法判断你这个是函数的声明还是实例化对象 //匿名对象的创建 A(2);//这个就是匿名对象,我们可以发现,在编译器在运行到下一行的时候就会调用析构函数,销毁这个匿名对象 //匿名对象的适合使用的场景,比如在,使用stl的时候,放进去vector容器,使用匿名对象,后序再说 return 0; }
拷贝对象的编译器的优化
前文,我们讲到了,对于同一行的代码连续调用构造函数(or拷贝构造函数),按照我们的理论会正常进行调用这两个函数,但是编译器对于这个做法,进行了优化,使得无论理论上多少次调用,但是经过优化,只需要调用一次构造函数
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 a1(1); A a2=a1;//我们这样是分开的,不会进行优化 //连续调用 f1(A(2));//这样的话,理论上我们就会先进行匿名构造,然后传值拷贝构造创建临时对象赋值给aa,所以是连续两次构造,编译器进行优化为1次 //当返回值为自定义类型A 的时候+赋值 A a3=f2();//先f2的返回值需要调用拷贝构造,返回一个临时对象,然后需要赋值给a3(再次调用拷贝构造) 所以 (aa返回)拷贝构造 + (a3赋值)拷贝构造 = 优化为一次构造(一步到位,临时对象直接给a3) return 0; }
首先我们要认识到赋值是什么,拷贝构造是什么,什么时候是赋值什么时候是拷贝构造
我们知道一点即可,是否已经创建了这个对象
如果早就创建了对象,那么就是赋值
如果没有,那么就是拷贝构造
new创建对象
对于c++、java这样的编程语言的话,那么我们一定知道new一个对象这样的说法,接下来先了解一下new
在C语言中我们去动态申请空间的时候,使用到malloc、calloc等函数,但是过程比较繁琐,需要强制类型转换等,对于C++来讲,我们也能动态申请空间,使用new关键字来实现
#include<malloc.h> int main() { //创建一个数组,动态申请空间 int* a1=(int*)malloc(sizeof(int)*10);//这样是C语言的方式 free(a1);//释放空间 //c++ int* a2=new int[10];//这样就可以啦 delete[] a2;//delete关键字来释放空间,要根据类型,这个是new [] 所以释放空间的时候跟着[] //new也可以做到初始化 int* a3=new int(10);//初始化数值为10,但是不能做到calloc那样,对于数组来进行全部的赋值(统一值) //int* a4=(int*)calloc(0,sizeof(int)*10)//a4[]数组初始化值都为0 int* a5=new int[5]{1,2,3};//这样对于数组进行初始化是可以的(使用大括号) //前三位为1 2 3 后两位为0 return 0; }
实际上可以实现数组元素全部为0,如int* pa=new int[10]{};//这样就可以实现了类似于calloc(0,sizeof(int)*10);一样的效果
当然除了内置类型可以new,自定义类型也可以,但是这两个都需要用对应类型的指针的方式来接收,因为new是申请的空间(堆空间),返回的也是地址
//演示样例 class A { public: A(int a, int b) :_a(a) , _b(b) {} void Print() { cout << _a << " " << _b << endl; } private: int _a, _b; }; int main() { A* a1 = new A(1, 2);//开空间+调用构造函数初始化 这个也进行了构造函数优化 a1->Print(); return 0; }