C++ 构造函数和析构函数
构造函数和析构函数
目录
页面
构造/和析构函数
问题
编译器为什么要求拷贝构造为什么要传引用?
如果存在两个 同类型的对象A a, b ,如果将a 拷贝给 b ,实际上call b类的拷贝构造,b对象的拷贝对象是如果是非引用形式。以 a() 实参, (A const A a)形参 ,这时是同一类型,他又回去call 形参的拷贝构造,形参还是(A const A a)形参 ,实参又去形参的拷贝构造 ,套娃。。。
Int & 不能拷贝给匿名对象上的
为什么要const A &a (45 被bind 后是没有绑定匿名对象上的,const 是为兼容bind 到匿名对象的 )
做一个逻辑上的常量限制。(从逻辑上是不允许被拷贝对象被修改的)
兼容const类型的拷贝 不允许直接吧const 类型的变量直接传到址非const的引用, 因为非const 是可以修改的。
构造函数与析构函数
构造/和析构函数 | 使用方式 | |
---|---|---|
默认构造函数 | ostream a; | |
ostream(string name ) | ostream a(xx) | |
ostream (const ostream &a) | 拷贝构造 于 = 不等价 | |
~ostream | 无 析构 |
每个对象创建时,一定call 构造函数,当然销毁也一定会调析构。
class Data{
public :
Data() {
cout << "default " << endl;
}
int x(){return __x;};
int y(){return __y;};
~Data(){
cout << "Free" <<endl;
}
private :
int __x,__y;
};
int main(){
Data dt;
cout <<dt.x() << dt.y() <<endl;
return 0;
}
// outoput
//default
//41969760
//Free
初始化列表
(初始化类中的成员)
初始化列表的顺序,和类中属性,声明的顺序有关系。而不是由初始化列表的顺序决定。
最终的意义就是→初始化每一个属性的行为。
按照约定,如果构造函数初始化完成所有的初始化列表 ,意味每个成员对象的属性已经初始化结束。
class A{
public :
A(){} // 一旦有带参数的构造器,如果没有调用按照规则调用构造函数的参树,则会报错
A(int x):x(x){
cout << "Class A:" << x <<endl;
}
int x ;
};
class Data{
public :
// 接在构造函数后面 : 初始列表(初始化成员)
//显示性的调用 A的构造函数
Data():__x(10),__y(0),a(34) {
cout << "default " << endl;
}
int x(){return __x;};
int y(){return __y;};
~Data(){
cout << "Free" <<endl;
}
private :
int __x,__y;
A a;
};
转换构造
一个参数构造函数又转换构造
这个赋值运算可以看做 将int转到A类型的对象,整形转换为一个A类型的对象.
class A{
public :
A(){} // 一旦有带参数的构造器,如果没有调用按照规则调用构造函数的参树,则会报错 需要显示指出构> A(int x):x(x){
cout << "Class A:" << x <<endl;
}
int x ;
};
int main(
A s(45);
A x =41;
return
)
//0x7fff1a506a40 Class A:45
// addressof a :0x7fff1a506a40
拷贝构造(这种都是浅拷贝,每一项成员依次拷贝过去)
系统会自动添加拷贝构造,即使没有声明拷贝构造.在调用时,也会依次调用
☕g++ -fno-elide-constructors 构造函数返回值优化 编译时需要带上参数才能编译出
class A{
public :
A(){
cout << " default" << endl;
} // 一旦有带参数的构造器,如果没有调用按照规则调用构造函数的参树,则会报错 需要显示指出构造函数
A(int x):x(x){
cout << this << " Class A:" << x <<endl;
}
A(const A &a){
//拷贝构造
cout << this << " :copy A" << &a <<endl;
}
int x ;
};
A a=45;
cout << "addressof a :" << &a <<endl;
0x7ffcfaaf6380 Class A:45
0x7ffcfaaf6370 :copy from A0x7ffcfaaf6380
addressof a :0x7ffcfaaf6370
第一行:调用为6380 的有参构造 ,这个小a的地址为 6370,也就是说这个0x7ffcfaaf6380 并非调的小a的有参构造
第二行,在调用第一行的有参构造函数后,第二行也会默认执行拷贝构造 ,将f6370拷贝给小a
☕首先这个**45 **会通过转换构造 去转换成一个匿名对象,匿名的对象为这个f6380对象,然后再将匿名的a对象拷贝6370对象
默认的赋值运算符
当赋值运算符出现的时候,起始出现的是拷贝构造 ,因为 这个a 还没声明好,所以使用的是拷贝构造,
而下面的而赋值运算符在赋值时,a 对象已经准备好了
class A{
public :
A(){
cout << " default" << endl;
} // 一旦有带参数的构造器,如果没有调用按照规则调用构造函数的参树,则会报错 需要显示指出构造函数
A(int x):x(x){
cout << this << " Class A:" << x << endl;
}
A(const A &a){
//拷贝构造
cout << this << " :copy A" << &a << endl;
}
void operator=(const A &a){
cout << this << "assign from " << &a << endl;
}
int x ;
};
0x7ffef092db80 Class A:45
0x7ffef092db70 :copy from A0x7ffef092db80
addressof a :0x7ffef092db70
0x7ffef092db90 Class A:78
0x7ffef092db70 assign from 0x7ffef092db90
int main(){
A a = 45;
cout << "addressof a :" << &a <<endl;
a = 78 ; //这个= 是相当于是call赋值运算符
return 0;
}
db80 通过转换构造 构造的对象 db90 也通过转换构造 构造的对象 (匿名的对象)
这个a的对象为 db70 通过 db80 拷贝过来 || 而这个db90 是通过赋值运算符赋值给 db70的
小的总结
A a = 78 由于这里的等号 ,一定call 调了拷贝构造 (而拷贝的出现的形式,是用的A类型的引用,这时会将45的参数(它本就不是a类型的值). 这时会通过转换构造,将45转换为匿名的临时对象,然后绑定到这个const A &a 的引用上)
a = 78 ; //这里就很明显,就调了 赋值运算符 (const A &a),后续过程一致。