C++对象模型-笔记三

构造函数,析构函数,复制构造函数
1、何时被调用
1、构造函数:

a.对于栈上的对象:在编译器遇到这样的定义时Point p;会调用Point的默认构造函数(如果定义了,或者合成了)

b.对于堆上的对象:new 之后如果成功申请空间,会调用默认构造函数

c.对于静态数据区的对象:程序装载的时候会调用默认构造函数,并且对于没有初始化的数据会初始化为0

b.对于堆上的对象:new 之后如果成功申请空间,会调用默认构造函数

c.对于静态数据区的对象:程序装载的时候会调用默认构造函数,并且对于没有初始化的数据会初始化为0
2、析构函数:

a.对于栈上的对象:在对象退出作用域的时候

b.对于堆上的对象:在delete 对象之前

c.对于静态数据区的对象:程序关闭的时候

3、复制构造函数(不一定是调用复制构造函数,有时候是直接进行位复制操作):

a.当明显的使用一个对象对另一个对象进行初始化操作的时候:X xx = x;

b.当使用函数使用对象作为参数值传递的时候:void foo(X xx);

c.当函数传回一个对象值的时候:X foo(){X xx;...;return xx;};
2、何时编译器会合成一个默认的
1、构造函数:(这个是编译器需要的)
a.当类中有一个或者多个成员对象带有默认构造函数时
b.当类的基类带有默认构造函数时
c.当类含有虚函数时:创建对象的vptr和vtbl
d.当类有一个虚基类时:创建虚基类的vbptr和vbtbl
2、析构函数:
a.当类的对象成员有析构函数时
b.当类的基类有析构函数时
3、复制构造函数:(合成之后除了位复制还需要其他操作)
a.当类中有一个或者多个成员对象有复制构造函数时(不管是定义还是合成的)
b.当类的基类有复制构造函数时
c.当类含有虚函数时
d.当类有一个或多个虚基类时
3、合成中的调用顺序
1、构造函数:(内到外)
a.所有的虚基类的构造函数被调用,从最深到最浅(每一个virtual base class subobject偏移必须在执行期被存取)
b.所有上一层的基类被调用,以声明顺序为顺序(多继承的时候需要调整this指针)
c.初始化该类的vptr和vtbl
d.初始化构造函数的初始化列表中的内容(如果有基类的构造参数,则在之前传过去)
e.调用未在初始化列表中的成员对象的构造函数
f.调用自定义代码
问题:虚基类构造的时候避免多次调用多次构造虚基类p211(添加_most_derived参数判断)
问题:为什么不能在构造函数里面调用虚函数(不会有多态效果)

2、析构函数:(外到内)
a.调用本身的析构函数
b.调用对象成员的析构函数,按声明的相反顺序
c.重新设定vptr指向适当的基类的vtbl
d.相反顺序调用基类析构函数
f.相反顺序调用虚基类的析构函数
3、复制构造函数:(赋值运算符的重载)
a.应该把虚基类的复制操作安插在最后,避免复制的时候被覆盖
4、NRV在VS中的实践(Debug和Release的区别)
class NRVTest{
public:
NRVTest(){cout << "NRVTest的构造函数" << endl;}
~NRVTest(){cout << "NRVTest的析构函数" << endl;}
NRVTest(const NRVTest&){cout << "NRVTest的复制构造函数" << endl;};
NRVTest copy(){
NRVTest n;
return n;
}
/*不进行NRV优化
void copy(NRVTest& _result){
NRVTest n //tmpObj
n::NRVTest();
_result::NRVTest(n);//复制构造
n::~NRVTest();//析构tmp
}
*/
/*NRV优化
void copy(NRVTest& _result){
_result::NRVTest();
return;
}
*/
};
int main(){
NRVTest obj;
NRVTest copyObj = obj.copy();
}


Debug模式下:不使用NRV

Release模式下:使用NRV


5、构造函数的初始化列表的作用(下面情况必须使用初始化列表)
a.当初始化一个ref member时
b.当初始化一个const member时
c.当调用一个基类的构造函数,需要一组参数时
d.当调用一个成员对象的构造函数,需要一组参数时
问题:为什么初始化成员对象要用初始化列表?

//想想这样初始化会发生什么
class Word{
public:
	Word(){_name=0;_cnt=0;}
private:
	String _name;
	int _cnt;
}


//被扩展
//产生多余的临时对象,并且调用拷贝
Word(){
	String tmp = String(0);
	_name.String::operator=(tmp);
	tmp::~String()


	_cnt = 0;
}


改成
Word::Word():_name(0){
	//则扩展成
	//_name:String(0);
	_cnt = 0;
}






---------------------------------------------------------------
1、验证构造函数和析构函数的调用时刻
class Con1{
public:
	int i;
};
class Con2{
public:
	Con2(){cout << "Con2的构造函数" << endl;}	//提供默认构造函数
	~Con2(){cout << "Con2的析构函数" << endl;};
	int i;
};
Con1 globalObj;
int main(){
	//验证3种初始化对象的情况,访问对象数据对象的结果
	Con1 obj1;
	Con2 obj2;
	//cout << "无默认构造函数的数据i: " << obj1.i << endl;	//链接错误
	cout << "有默认构造函数的数据i: " << obj2.i << endl;
	cout << "全局对象无默认构造函数的数据i: " << globalObj.i << endl;


	//验证对象的构造函数和析构函数的调用时刻
	{
		Con2 obj2;
	};
	cout << "离开作用域之后" << endl;
	Con2* pObj = new Con2();
	pObj->i = 100;
	cout << "pObj->i: " << pObj->i << endl;
	cout << "delete pObj之后" << endl;
	delete pObj;
	cout << "delete pObj之后的I值" << pObj->i << endl;
	pObj = NULL;
}




2、析构函数什么时候要声明为虚函数
class ClxBase{
public:
    ClxBase() {};
    ~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;};


    void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};


class ClxDerived : public ClxBase{
public:
    ClxDerived() {};
    ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };


    void DoSomething() { cout << "Do something in class ClxDerived!" << endl; }
};
 int   main(){  
 ClxBase *p =  new ClxDerived;
 p->DoSomething();
 delete p;
 return 0;
 } 


 这种情况会造成内存泄漏,因为调用析构函数的时候,只调用了基类的析构函数。
 但如果将基类的析构函数声明为虚函数,则可以调用到子类的析构函数
 如果不需要基类对派生类及对象进行操作,则不能定义虚函数,因为这样会增加内存开销


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值