目录
- 再谈构造函数
- 初始化列表
- 初始化列表解决的问题:
- 静态成员函数、成员变量
- explicit关键字
- 友元
- 友元函数
- 友元类
- 内部类
- 编译器的常见优化(了解)
- 优化1
再谈构造函数
初始化列表
有一些成员变量是无法在函数体内初始化的,eg:引用、const、自定义类型没有默认构造
Date(int year = 1, int month = 1, int day = 1)
{
//函数体内初始化
_year = year;
_month = month;
_day = day;
}
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{
//初始化列表初始化
}
成员变量实在类里面声明的
如下图所示,因此对于引用、const只允许在定义的时候初始化,所以必须在初始化列表这定义
Date(int year = 1, int month = 1, int day = 1)
:_ref(year)
,_n(1)
{
_year = year;
_month = month;
_day = day;
}
对于上述代码
剩余的3个成员没有在初始化列表显示写出来定义
但是他也会定义,只是内置类型默认给的随机值
如果是自定义类型成员会去调用它的默认构造函数(如果没有默认构造函数就是编译不通过)
此时A这个类有默认构造函数(即不需要参数的构造函数),那么对于_aa不需要显示的写定义,他就会自动去调用它的默认构造函数
class A
{
public:
A(int a = 1)
{
_a = a;
cout<<"A(int a)";
}
private:
int _a;
};
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_ref(year)
,_n(1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
int& _ref;
const int _n;
A _aa;
};
此时A这个类没有默认构造函数,对于_aa来说就必须在初始化显示的定义了
class A
{
public:
A(int a)
{
_a = a;
cout<<"A(int a)";
}
private:
int _a;
};
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_ref(year)
,_n(1)
,_aa(1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
int& _ref;
const int _n;
A _aa;
};
对于下面中 int _year = 1;给的是缺省值,这样即使没有显示的在初始化列表中定义,_year也不会被初始化成随机值,而是初始化成1
private:
int _year = 1;
int _month;
int _day;
int& _ref;
const int _n;
A _aa;
初始化列表解决的问题:
1、必须在定义的地方显示初始化,引用、const、没有默认构造的自定义成员
2、有些自定义成员想要显示初始化,自己控制
尽量使用初始化列表初始化
构造函数能不能只要初始化列表,不要函数体初始化
不能,因为有些初始化或者检查的工作,初始化列表不能完全搞定
例题
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();
}
A. 输出1 1
B.程序崩溃
C.编译不通过
D.输出1 随机值
答案:D
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
静态成员函数、成员变量
静态成员函数指定类域就行
静态成员函数不能访问非静态的成员变量或函数,因为没有this
问题:计算到底创建了多少个A类的对象
对于上述问题我们可以在该类里面创建一个静态成员变量,在每次的构造函数或拷贝构造的时候++
class A
{
public:
A(int a)
:_a(a)
{
count++;
}
A(const A& y)
{
_a = y._a;
count++;
}
private:
int _a;
static int count;
};
int A::count = 0;
注意:
静态成员变量在类里面声明,在类外面定义
不能在类声明的时候给缺省值,因为塔不走初始化列表,缺失值是给初始化列表的
它不属于任何一个类的对象,它是属于整个类的
但是count是私有的在类外面无法访问
方法一:将count设置成公有
方法二:写一个get函数
这样的缺点就是,非静态的成员函数必须用对象调用
解决上述问题的方法:(1)建一个对象或者匿名对象(2)将Get函数写成静态成员函数
(1)匿名对象
匿名对象的作用域只有一行
(2)静态成员函数
因为静态成员函数没有this指针,所以可以不用创建对象,直接指定类域就行
对于静态成员变量,在算类对象的大小时是不算的,这个类创建的对象大小是4字节
explicit关键字
整型和指针不能隐式转,只能强制转
对于自定义类型,若该类是单参数的构造函数,或者多参数的构造函数(也可以,用半缺省只用一个参数初始化),或者是全缺省参数,或者需要多参构造函数
(1)int/double单参数的构造函数
class A
{
public:
A(int a = 0)
:_a(a)
{}
A(const A& y)
{
_a = y._a;
}
private:
int _a;
};
int main()
{
A aa = 3;
A bb = 3.1;
//逗号表达式是具有返回值,返回值是最后一个数字
A cc = (2024, 3, 29); //等价于 A cc = 3;
return 0;
}
(2)用半缺省只用一个参数初始化
class A
{
public:
A(int a, int b = 1,int c = 1)
:_b(b)
,_c(c)
{}
A(const A& y)
{
_a = y._a;
}
private:
int _a;
int _b;
int _c;
};
int main()
{
A aa = 3;
A bb = 3.1;
return 0;
}
(3)全缺省参数的构造函数
class A
{
public:
A(int a = 1, int b = 1,int c = 1)
:_a(a)
,_b(b)
,_c(c)
{}
A(const A& y)
{
_a = y._a;
}
private:
int _a;
int _b;
int _c;
};
int main()
{
A aa = 3;
A bb = 3.1;
return 0;
}
(4)需要多参构造函数
class A
{
public:
A(int a, int b,int c)
:_a(a)
,_b(b)
,_c(c)
{}
A(const A& y)
{
_a = y._a;
}
private:
int _a;
int _b;
int _c;
};
int main()
{
A aa = {2024, 3, 29};
return 0;
}
不想让自定义类型和内置类型的隐式类型的转换,构造函数+explicit(但强转可以)
强转
隐式类型转换可以用于list模板的创建
#include<vector>
#include<list>
#include<iostream>
using namespace std;
class A
{
public:
A(int a)
:_a(a)
{}
A(const A& y)
{
_a = y._a;
}
private:
int _a;
int _b;
int _c;
};
int main()
{
//A aa = {2024, 3, 29};
list<A> lt;
A aa(1);
lt.push_back(aa);
lt.push_back(A(2));
lt.push_back(3);
return 0;
}
友元
在类外面突破封装,去访问私有保护的一种方式
友元函数
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同
友元类
友元类的定义
friend + 类的声明
我想访问你,把我定义成你的友元
友元是单向关系
class A
{
public:
friend class B;
private:
int _a;
};
class B
{
private:
int _b;
};
内部类
class A
{
private:
int _a;
public:
class B
{
private:
int _b;
};
};
如果一个类定义在另一个类的内部,这个内部类就叫做内部类
内部类就是外部类的友元类
A和B的关系:B就是一个普通类,只是受A的类域和访问限定符限制,跟静态很像
B是A的友元,B能访问A 的成员,A不能访问B的成员
A的大小不算内部类
因为B在A 里面是public,所以在外面是可以访问的,但如果B在A的里面是private,那么B这个类只能在A 类里面去访问,外面的不能访问
A::B bb;
编译器的常见优化(了解)
不同编译器可能会不同
优化1
A aa1 = 1;
1、先用1构造一个临时对象
2、再用临时对象拷贝构造aa1
但实际上只用了一次构造函数
同一表达式中,连续的构造+构造/构造+拷贝构造/拷贝构造+拷贝构造会二合一
构造+构造- >构造
构造+拷贝构造 -> 构造
拷贝构造+拷贝构造 -> 拷贝构造
class A
{
public:
A(int a)
:_a(a)
{
cout << "A(int a)" << endl;
}
A(const A& i)
{
_a = i._a;
cout << "A(const A& i)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
A aa = 1;
return 0;
}
下面这种情况就没有合二为一,因为不在同一表达式,编译器不敢
在同一表达式,编译器就会优化