原创请注明出处:
我们这里主要讨论构造函数的构造函数的分类和其调用时机
测试类如下:
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;