目录
1.再谈构造函数
class C
{
public:
C(int grade)//这个不是默认构造
:_grade(grade)
{}
private:
int _grade;
};
class A
{
public:
A(int grade)
:_grade(grade)
, _a(3)
, _b(_grade)
, _c(99)
{
cout << "A(int grade)" << endl;
}
A(const A& x)//这个const注意
:_grade(x._grade)
, _a(x._a)
, _b(x._b)
, _c(x._c)
{
cout << "A(const A& x)" << endl;
}
private:
int _grade;
const int _a;
int& _b = _grade;
//C _c(10);
C _c = 10;
};
int main()
{
A a(10);
return 0;
}
2.初始化列表
为了解决:const ,引用类型没有默认构造可以用,自定义类型可能会没有默认构造可以用
2.1定义
①初始化列表是成员变量定义的地方,在构造函数(拷贝构造也是构造)中使用,所有的成员都会走初始化列表
②定义顺序为成员变量的声明顺序
③在初始化列表的成员就在初始化列表里定义,没有的再定义成缺省值
④初始化列表只能出现一次成员,不然会报错
其实说来说去还是对这句话的理解:
如果这个成员没有在初始化列表:内置类型不做处理(除非给了缺省值),对自定义类型调用它的默认构造
2.2分析代码
C类是没有写默认构造
错误①
我刚开始出现这个问题,这个C类要在A类上面定义
错误②
C _c(10),会有语法错误,C _c; 这样也不行,因为C类没有默认构造,什么事默认构造:类和对象上是引入了初始化列表的一个原因,因为没法初始化没有默认构造的自定义类型
3.隐式类型转换
3.1单参数(C++98)
对于C _c = 10; 来说,这里有个隐式类型的转换,对于单个参数的构造函数,支持隐式类型转换
int main()
{
A a1(10); //1
A a2 = 2; //2
A& a3 = 10; //3
const A& a4 = 10; //4
return 0;
}
①单参数的构造函数
②先构造函数初始化临时对象,在执行拷贝构造 优化为构造
③临时对象具有常性,编译错误
④const具有常性,这里引用了临时对象,所以编译器没法优化(新一点的编译器会优化掉)
3.2双参数(C++11)
没有区别,两个参数用{},大括号括起来就行;例:A a = { 2, 2 };
3.3explicit关键字
explicit A(int grade)
在构造函数的前面加上explicit就行,这样就会禁止隐式类型的转换
4.Static成员
比如说我们要统计创建对象的次数
class A
{
public:
A(int grade)
://...
{
//...
count++;
}
private:
int _grade;
const int _a;
int& _b = _grade;
C _c = 10;
static int count;
};
int A::count = 0;//类外定义,可以不初始化
我就把关键部分写下,这样会出现一个问题,我们必须要有对象才能知道创建过的对象的个数。
例子如下
void func()
{
A a[10];
}
int main()
{
func();
A a;
cout<< a.count - 1<<endl;
return 0;
}
我们还要在类内加一个默认构造,直接用全缺省的默认构造 :A(int grade = 0)
其实很麻烦①在类内加一个GetCount()的函数,为了知道个数②还有在定义一个对象③GetCount还要减一
所以引入新的办法:静态函数
样例如下
通过类或者对象直接读取count(private修饰的话,不能读取)所以通过静态成员函数
class A
{
public:
static int GetCount()
{
return count;
}
private:
static int count;
};
int A::count = 0;
int main()
{
A::GetCount;
A a;
a.GetCount();
}
4.1特性
①静态成员函数就是为了静态成员变量而生的
②静态成员函数不能访问(读写)非静态成员变量(因为没有隐藏的this指针)
③静态成员属于所有的对象,属于整个类
④同样有访问权限,不能被const修饰
5.友元
增加耦合度,破坏封装,不建议多用
5.1友元函数
在类内声明后,友元函数可以直接访问类的私有成员
class A
{
friend void func();
public:
private:
}
void func()
{
A a;
cout << a._grade;
}
①友元声明可以放在类里面的任何地方
②友元函数不能用const修饰
③友元声明可以放在多个类里
5.2友元类
class A
{
friend class B;
public:
private:
int _grade;
};
class B
{
public:
int func(A a)
{
return a._grade;
}
private:
};
①友元关系是单项的
②友元关系不能传递
总结:你看 类A 里面有个B ,就好比A给了B一把钥匙,那么B就可以访问它,而B,没有给A,就是这么合理
6.内部类(很少用)
class A
{
public:
class B
{
public:
int func(A a)
{
count++;
a._grade++;
return a._grade;
//_grade++; ×
}
private:
};
private:
int _grade;
static int count;
//...
};
int A::count = 0;
①内部类受访问限定符的限制
②非静态成员必须与特定的对象相对,(用对象,例: a._grade++, 直接_grade找不到)
③sizeof的大小是外部类,和内部类无关
总结:内部类是外部类的友元,内部类由外部类的钥匙
7.匿名对象
7.1特性
class A
{
public:
static int GetCount()
{
return count;
}
void func()
{
}
private:
int _grade;
const int _a;
int& _b = _grade;
C _c = 10;
static int count;
};
int A::count = 0;
int main()
{
A().func();
A().GetCount();
}
①生命周期只在这一行
②如果func是private,不能访问
③创建匿名对象,会去调用拷贝构造
④匿名对象具有常性
8.编译器的优化
8.1传参优化
void func1(A a)
{
}
void func2(A& a)
{
}
void func3(const A& a)
{
}
int main()
{
A a = 1; //1 构造+拷贝->构造
func1(a); //2 拷贝
func1(1); //3 构造+拷贝->构造
func1(A()); //4 构造+拷贝->构造
func2(a); //5 直接传
func2(1); //6 临时对象具有常性,也就是临时对象不能改变,计算const修饰
func2(A()); //7 同6
func3(a); //8 直接传
func3(1); //9 构造+拷贝
func3(A()); //10 构造+拷贝
return 0;
}
9和10不能优化,这个临时对象必须有,不然怎么引用
总结:接收返回值,尽量拷贝构造接收,不要赋值接收
8.2返回优化
A func1()
{
A a;
return a;
}
A func2()
{
return A();
}
int main()
{
func1();//构造+拷贝(拷贝给临时对象)
A a1 = func1();//构造+拷贝+拷贝->构造+拷贝
func2();//构造+拷贝->构造//你这么想他构造出来的是临时对象,拷贝构造出来的也是临时对象
A a2 = func2();//构造+拷贝+拷贝->构造
}
总结:函数返回对象时,尽量返回匿名对象
注:新的编译器调试不出效果,它优化的厉害