再論C++建構函式分類和呼叫時機以及匿名物件
原創請註明出處:
我們這裡主要討論建構函式的建構函式的分類和其呼叫時機
測試類如下:
namespace test_n
{
int A_G=0;
class test
{
public:
test() //無引數建構函式,如果沒有呼叫預設建構函式
{
cout<<"無參建構函式呼叫"<<" adress: "<<&a<<endl;
}
test(int c_a,int c_b)//有參建構函式
{
cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
a=c_a;
b=c_b;
}
test(int c_a)
{
cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
a=c_a;
}
test(const test &m) //copy 建構函式
{
cout<<"copy建構函式呼叫"<<" adress: "<<&a<<endl;
a = m.a;
b = m.b;
}
void print()
{
cout<<a<<" "<<b<<endl;="" ="" }=""
~test()
{
cout<<"解構函式呼叫"<<" adress: "<<&a<<endl;
A_G++;
cout<<a_g<<endl;
}
void plus20()
{
a = a+20;
b = b+20;
}
//3-3
void printany(test p)
{
cout<<"匿名物件生成"<<endl;
p.print();
}
void printany(test* p)
{
cout<<"無名物件生成"<<endl;
p->print();
}
void printanys(test& p)
{
cout<<"無名物件生成"<<endl;
p.print();
}
//3-4
test test34(test p)
{
p.plus20();
return p;
}
private:
int a;
int b;
};
}
1、預設建構函式和無參建構函式
實際上預設建構函式就是什麼都不做的無參建構函式,這個時候如果不初始化會私有變數的值會出現一些垃圾值
如:
test() //無引數建構函式
{
cout<<"無參建構函式呼叫"<<" adress: "<<&a<<endl;
}
這樣成員變數a和b就會出現垃圾值
呼叫時機:
test a; //1-1無引數建構函式呼叫test(),如果沒有呼叫預設建構函式
2、有參建構函式
如下都是有參建構函式:
test(int c_a,int c_b)//有參建構函式
{
cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
a=c_a;
b=c_b;
}
test(int c_a)//有參建構函式
{
cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
a=c_a;
}
呼叫時機:
test b(1,2);//2-1有參建構函式呼叫test(int c_a,int c_b)
b.print();
test c = (100,100);//2-2有參建構函式呼叫100,100後面的這一個100忽略,逗號表示式,呼叫test(int c_a)
c.print();
test d = test(100,100);//2-3有參建構函式呼叫test(int c_a,int c_b),先生成一個匿名物件 然後被賦予給d,只呼叫一次構造和解構函式
d.print();
3、COPY建構函式(也叫賦值建構函式)
如下:
test(const test &m) //copy 建構函式
{
cout<<"copy建構函式呼叫"<<" adress: "<<&a<<endl;
a = m.a;
b = m.b;
}
copy建構函式呼叫時機有4種其中有2個難點就是,將物件作為實參和物件作為返回值的時候
呼叫時機:
3-1:用其他物件初始化賦值操作
test e = b;//3-1copy建構函式呼叫test(const test &m),用b物件來初始化e 注意是初始化而不是等號操作
e.print();
3-2:使用COPY建構函式原型,實參為其他物件
test f(d);
f.print();//3-2copy建構函式呼叫test(const test &m),用d物件來初始化f
3-3:難點作為函式實參,會生成匿名物件
f.printany(d);//3-3copy建構函式呼叫test(const test &m),用d物件來初始化一個匿名物件,這個匿名物件在printany函式生命週期中使用,然後析構掉
3-4:難點作為函式返回值,會生成匿名物件,當作為返回值的時候匿名物件又要考慮到如何去接這個返回值分3種如下
d.test34(d);//3-4 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,最後呼叫函式無接就析構掉
。
test m = d.test34(d);//3-5 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,最後用於初始化
,因為匿名物件記憶體已經分配,就直接用匿名物件的記憶體給物件m即可,它的析構隨m的使用完成而析構,避免不必要的記憶體分配和析構提高效能
m.print();
c = d.test34(d); //3-6 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,雖然有接,但是這個
使用等於符號過載操作,因為c已經有了記憶體空間,那麼也會及時析構掉
c.print();
總結:實際上建構函式呼叫一定要分清是否是初始化和等號操作,等號操作呼叫的是運算子過載,而初始化是要考慮到記憶體空間的分配的,我們的構造行數都是在初始化的時候呼叫的。
關於匿名物件需要考慮到記憶體分配,比如作為實參C++編譯器自然要生成一個匿名物件在函式生命週期中使用,完成後自然要析構掉,如果作為一個返回值,自然也要生成一個匿名
物件,關鍵是看你如何去接,如果初始化方法去接這個匿名物件的記憶體將會被"轉正",如果不接受或者不是初始化則析構掉,因為沒有用了。
下面是全部的測試程式碼以及註釋
執行結果:
無參建構函式呼叫 adress: 0x7ffc20853490
1771925424 32660
有參建構函式呼叫 adress: 0x7ffc208534a0
1 2
有參建構函式呼叫 adress: 0x7ffc208534b0
100 0
有參建構函式呼叫 adress: 0x7ffc208534c0
100 100
copy建構函式呼叫 adress: 0x7ffc208534d0
1 2
copy建構函式呼叫 adress: 0x7ffc208534e0
100 100
copy建構函式呼叫 adress: 0x7ffc208534f0
匿名物件生成
100 100
解構函式呼叫 adress: 0x7ffc208534f0
1
copy建構函式呼叫 adress: 0x7ffc20853500
copy建構函式呼叫 adress: 0x7ffc20853510
解構函式呼叫 adress: 0x7ffc20853510
2
解構函式呼叫 adress: 0x7ffc20853500
3
copy建構函式呼叫 adress: 0x7ffc20853530
copy建構函式呼叫 adress: 0x7ffc20853520
解構函式呼叫 adress: 0x7ffc20853530
4
120 120
copy建構函式呼叫 adress: 0x7ffc20853540
copy建構函式呼叫 adress: 0x7ffc20853550
解構函式呼叫 adress: 0x7ffc20853550
5
解構函式呼叫 adress: 0x7ffc20853540
6
120 120
解構函式呼叫 adress: 0x7ffc20853520
7
解構函式呼叫 adress: 0x7ffc208534e0
8
解構函式呼叫 adress: 0x7ffc208534d0
9
解構函式呼叫 adress: 0x7ffc208534c0
10
解構函式呼叫 adress: 0x7ffc208534b0
11
解構函式呼叫 adress: 0x7ffc208534a0
12
解構函式呼叫 adress: 0x7ffc20853490
13
作者微信:
</endl;
</endl;
</endl;
</endl;
</endl;
</endl;
</endl;
</a_g<<endl;
</endl;
</a<</endl;
</endl;
</endl;
</endl;
我們這裡主要討論建構函式的建構函式的分類和其呼叫時機
測試類如下:
namespace test_n
{
int A_G=0;
class test
{
public:
test() //無引數建構函式,如果沒有呼叫預設建構函式
{
cout<<"無參建構函式呼叫"<<" adress: "<<&a<<endl;
}
test(int c_a,int c_b)//有參建構函式
{
cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
a=c_a;
b=c_b;
}
test(int c_a)
{
cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
a=c_a;
}
test(const test &m) //copy 建構函式
{
cout<<"copy建構函式呼叫"<<" adress: "<<&a<<endl;
a = m.a;
b = m.b;
}
void print()
{
cout<<a<<" "<<b<<endl;="" ="" }=""
~test()
{
cout<<"解構函式呼叫"<<" adress: "<<&a<<endl;
A_G++;
cout<<a_g<<endl;
}
void plus20()
{
a = a+20;
b = b+20;
}
//3-3
void printany(test p)
{
cout<<"匿名物件生成"<<endl;
p.print();
}
void printany(test* p)
{
cout<<"無名物件生成"<<endl;
p->print();
}
void printanys(test& p)
{
cout<<"無名物件生成"<<endl;
p.print();
}
//3-4
test test34(test p)
{
p.plus20();
return p;
}
private:
int a;
int b;
};
}
1、預設建構函式和無參建構函式
實際上預設建構函式就是什麼都不做的無參建構函式,這個時候如果不初始化會私有變數的值會出現一些垃圾值
如:
test() //無引數建構函式
{
cout<<"無參建構函式呼叫"<<" adress: "<<&a<<endl;
}
這樣成員變數a和b就會出現垃圾值
呼叫時機:
test a; //1-1無引數建構函式呼叫test(),如果沒有呼叫預設建構函式
2、有參建構函式
如下都是有參建構函式:
test(int c_a,int c_b)//有參建構函式
{
cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
a=c_a;
b=c_b;
}
test(int c_a)//有參建構函式
{
cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
a=c_a;
}
呼叫時機:
test b(1,2);//2-1有參建構函式呼叫test(int c_a,int c_b)
b.print();
test c = (100,100);//2-2有參建構函式呼叫100,100後面的這一個100忽略,逗號表示式,呼叫test(int c_a)
c.print();
test d = test(100,100);//2-3有參建構函式呼叫test(int c_a,int c_b),先生成一個匿名物件 然後被賦予給d,只呼叫一次構造和解構函式
d.print();
3、COPY建構函式(也叫賦值建構函式)
如下:
test(const test &m) //copy 建構函式
{
cout<<"copy建構函式呼叫"<<" adress: "<<&a<<endl;
a = m.a;
b = m.b;
}
copy建構函式呼叫時機有4種其中有2個難點就是,將物件作為實參和物件作為返回值的時候
呼叫時機:
3-1:用其他物件初始化賦值操作
test e = b;//3-1copy建構函式呼叫test(const test &m),用b物件來初始化e 注意是初始化而不是等號操作
e.print();
3-2:使用COPY建構函式原型,實參為其他物件
test f(d);
f.print();//3-2copy建構函式呼叫test(const test &m),用d物件來初始化f
3-3:難點作為函式實參,會生成匿名物件
f.printany(d);//3-3copy建構函式呼叫test(const test &m),用d物件來初始化一個匿名物件,這個匿名物件在printany函式生命週期中使用,然後析構掉
3-4:難點作為函式返回值,會生成匿名物件,當作為返回值的時候匿名物件又要考慮到如何去接這個返回值分3種如下
d.test34(d);//3-4 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,最後呼叫函式無接就析構掉
。
test m = d.test34(d);//3-5 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,最後用於初始化
,因為匿名物件記憶體已經分配,就直接用匿名物件的記憶體給物件m即可,它的析構隨m的使用完成而析構,避免不必要的記憶體分配和析構提高效能
m.print();
c = d.test34(d); //3-6 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,雖然有接,但是這個
使用等於符號過載操作,因為c已經有了記憶體空間,那麼也會及時析構掉
c.print();
總結:實際上建構函式呼叫一定要分清是否是初始化和等號操作,等號操作呼叫的是運算子過載,而初始化是要考慮到記憶體空間的分配的,我們的構造行數都是在初始化的時候呼叫的。
關於匿名物件需要考慮到記憶體分配,比如作為實參C++編譯器自然要生成一個匿名物件在函式生命週期中使用,完成後自然要析構掉,如果作為一個返回值,自然也要生成一個匿名
物件,關鍵是看你如何去接,如果初始化方法去接這個匿名物件的記憶體將會被"轉正",如果不接受或者不是初始化則析構掉,因為沒有用了。
下面是全部的測試程式碼以及註釋
點選(此處)摺疊或開啟
-
/*************************************************************************
-
> File Name: test.cpp
-
> Author: gaopeng QQ:22389860 all right reserved
-
> Mail: gaopp_200217@163.com
-
> Created Time: Fri 24 Mar 2017 08:19:28 PM CST
-
************************************************************************/
-
-
#include<iostream>
-
using namespace std;
-
namespace test_n
-
{
-
int A_G=0;
-
class test
-
{
-
public:
-
test() //無引數建構函式,如果沒有呼叫預設建構函式
-
{
-
cout<<"無參建構函式呼叫"<<" adress: "<<&a<<endl;
-
}
-
test(int c_a,int c_b)//有參建構函式
-
{
-
cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
-
a=c_a;
-
b=c_b;
-
-
}
-
test(int c_a)
-
{
-
cout<<"有參建構函式呼叫"<<" adress: "<<&a<<endl;
-
a=c_a;
-
}
-
-
test(const test &m) //copy 建構函式
-
{
-
cout<<"copy建構函式呼叫"<<" adress: "<<&a<<endl;
-
a = m.a;
-
b = m.b;
-
}
-
-
void print()
-
{
-
cout<<a<<" "<<&a<<" "<<b<<endl;
-
}
-
-
~test()
-
{
-
cout<<"解構函式呼叫"<<" adress: "<<&a<<endl;
-
A_G++;
-
cout<<A_G<<endl;
-
}
-
void plus20()
-
{
-
a = a+20;
-
b = b+20;
-
}
-
//3-3
-
void printany(test p)
-
{
-
cout<<"匿名物件生成"<<endl;
-
p.print();
-
}
-
void printany(test* p)
-
{
-
cout<<"無匿名物件生成"<<endl;
-
p->print();
-
}
-
void printanys(test& p)
-
{
-
cout<<"無匿名物件生成"<<endl;
-
p.print();
-
}
-
-
//3-4
-
test test34(test p)
-
{
-
// cout<<"2個匿名物件生成,看如何接"<<endl;
-
p.plus20();
-
return p;
-
}
-
//test new
-
test& testy(test p) //返回為區域性的匿名物件的引用,不能做左值不能用於初始化引用
-
{
-
p.plus20();
-
return p;
-
}
-
-
test& testm()//返回是引用記憶體空間永久可以作為左值
-
{
-
static test yy;
-
yy.print();
-
return yy;
-
}
-
-
private:
-
int a;
-
int b;
-
-
};
-
}
-
-
//
-
int main()
-
{
-
using namespace test_n;
-
test a; //1-1無引數建構函式呼叫test()
-
a.print();
-
test b(1,2);//2-1有參建構函式呼叫test(int c_a,int c_b)
-
b.print();
-
test c = (100,100);//2-2有參建構函式呼叫100,100後面的這一個100忽略,呼叫test(int c_a)
-
c.print();
-
test d = test(100,100);//2-3有參建構函式呼叫test(int c_a,int c_b),先生成一個匿名物件 然後被賦予給d,只呼叫一次構造和解構函式
-
d.print();
-
-
test e = b;//3-1copy建構函式呼叫test(const test &m),用b物件來初始化e
-
e.print();
-
-
test f(d);
-
f.print();//3-2copy建構函式呼叫test(const test &m),用d物件來初始化f
-
-
f.printany(d);//3-3copy建構函式呼叫test(const test &m),用d物件來初始化一個匿名物件,這個匿名物件在printany函式生命週期中使用,然後析構掉
-
-
//f.printanys(d);**如果呼叫為引用當然不需要匿名物件建立了,因為這是傳引用
-
//f.printanys(&d);**如果呼叫為指標也不需要匿名物件的建立,因為傳入是指標
-
-
d.test34(d);//3-4 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,最後呼叫函式無接就析構掉
-
。
-
test m = d.test34(d);//3-5 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,最後用於初始化
-
,因為匿名物件記憶體已經分配,就直接用匿名物件的記憶體給物件m即可,它的析構隨m的使用完成而析構,避免不必要的記憶體分配和析構提高效能
-
m.print();
-
-
c = d.test34(d); //3-6 呼叫了2次copy建構函式,第一個匿名物件是傳值物件d和上面一樣肯定生命週期完成要析構掉,第二次是return p返回一個匿名物件,雖然有接,但是這個
-
使用等於符號過載操作,因為c已經有了記憶體空間,那麼也會及時析構掉
-
c.print();
-
-
}
-
-
//以下是函式返回引用作為左值和右值的測試
-
//--函式返回用用分為以下
-
//--返回為區域性變數
-
//可以用int a = test();來接
-
//不可以用int &a = test();來接
-
//不能做左值
-
//
-
//--返回為全域性或者靜態變數
-
//可以用int a = test();來接
-
//可以用int &a = test();來接
-
//可以當左值
-
//
-
//a=test() 1 變數來接
-
//int &a=test() 2 初始引用來接
-
//test()=b 3 作為左值
-
//int& test()
-
//{
-
// static int b ;
-
// return b;
-
//}
-
int main12()
-
{
-
using namespace test_n;
-
test b(1,2);//2-1有參建構函式呼叫test(int c_a,int c_b)
-
-
// test a = b.testy(b);
-
// test &c = b.testy(b);
-
-
test m ;
-
m = b.testy(b);
-
m.print();
-
m.testm() = b;//左值必須為static,及記憶體要永久
-
m.testm();
-
// a.print();
-
// c.print();
- }
執行結果:
無參建構函式呼叫 adress: 0x7ffc20853490
1771925424 32660
有參建構函式呼叫 adress: 0x7ffc208534a0
1 2
有參建構函式呼叫 adress: 0x7ffc208534b0
100 0
有參建構函式呼叫 adress: 0x7ffc208534c0
100 100
copy建構函式呼叫 adress: 0x7ffc208534d0
1 2
copy建構函式呼叫 adress: 0x7ffc208534e0
100 100
copy建構函式呼叫 adress: 0x7ffc208534f0
匿名物件生成
100 100
解構函式呼叫 adress: 0x7ffc208534f0
1
copy建構函式呼叫 adress: 0x7ffc20853500
copy建構函式呼叫 adress: 0x7ffc20853510
解構函式呼叫 adress: 0x7ffc20853510
2
解構函式呼叫 adress: 0x7ffc20853500
3
copy建構函式呼叫 adress: 0x7ffc20853530
copy建構函式呼叫 adress: 0x7ffc20853520
解構函式呼叫 adress: 0x7ffc20853530
4
120 120
copy建構函式呼叫 adress: 0x7ffc20853540
copy建構函式呼叫 adress: 0x7ffc20853550
解構函式呼叫 adress: 0x7ffc20853550
5
解構函式呼叫 adress: 0x7ffc20853540
6
120 120
解構函式呼叫 adress: 0x7ffc20853520
7
解構函式呼叫 adress: 0x7ffc208534e0
8
解構函式呼叫 adress: 0x7ffc208534d0
9
解構函式呼叫 adress: 0x7ffc208534c0
10
解構函式呼叫 adress: 0x7ffc208534b0
11
解構函式呼叫 adress: 0x7ffc208534a0
12
解構函式呼叫 adress: 0x7ffc20853490
13
作者微信:
</endl;
</endl;
</endl;
</endl;
</endl;
</endl;
</a_g<<endl;
</endl;
</a<</endl;
</endl;
</endl;
</endl;
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2134726/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C++:建構函式的分類和呼叫C++函式
- 在 C++ 中子類繼承和呼叫父類的建構函式方法C++繼承函式
- 在C++中子類繼承和呼叫父類的建構函式方法C++繼承函式
- C++ 類建構函式和解構函式C++函式
- C++ 類中呼叫基類帶引數建構函式以及常量如何初始化C++函式
- C++物件導向程式設計 類和物件提高篇 建構函式C++物件程式設計函式
- [C++]顯示呼叫建構函式和解構函式C++函式
- C++建構函式和解構函式呼叫虛擬函式時使用靜態聯編C++函式
- C++再議建構函式及複製建構函式深度複製C++函式
- C++學習筆記-----類和建構函式C++筆記函式
- C++語言之結構體、類、建構函式、拷貝建構函式C++結構體函式
- C/C++——建構函式、複製建構函式和解構函式的執行時刻C++函式
- C++ 建構函式和解構函式C++函式
- c++內建函式物件C++函式物件
- 【C++】建構函式C++函式
- C++建構函式C++函式
- c# tcbs之建構函式呼叫建構函式示例C#函式
- C++中建構函式,拷貝建構函式和賦值函式的詳解C++函式賦值
- 類的建構函式和解構函式函式
- 建構函式建立物件函式物件
- Day 14 匿名函式 內建函式 物件導向程式設計函式物件程式設計
- 類函式和物件函式 PHP函式物件PHP
- js Date()建構函式建立時間日期物件JS函式物件
- 輕鬆理解建構函式和原型物件函式原型物件
- C++ 禁用類的複製建構函式和賦值運算子C++函式賦值
- PHP 手冊 (類與物件) 學習筆記五:建構函式和解構函式PHP物件筆記函式
- 父類和子類的建構函式問題函式
- 建構函式之間的呼叫函式
- Servlet呼叫構造方法,物件傳不進建構函式中去問題Servlet構造方法物件函式
- Javascript 物件導向中的建構函式和原型物件JavaScript物件函式原型
- C++ 類建構函式初始化列表介紹C++函式
- C++入門記-建構函式和解構函式C++函式
- 【C++】初始化列表建構函式VS普通建構函式C++函式
- 建立派生類物件,建構函式的執行順序物件函式
- 深入分析C++物件模型之移動建構函式C++物件模型函式
- C++箴言:避免解構函式呼叫虛擬函式C++箴言函式
- JS 建構函式與類JS函式
- 【C++】類初探常量成員函式與建構函式初始值列表C++函式