《C++新经典对象模型》之第2章 对象
- 2.1 类对象(实例)占用空间
- 02.01.cpp
- 2.2 对象结构的发展和演化
- 02.02.cpp
- 2.3 this指针调整
- 02.03.cpp
- 2.4 分析obj(目标文件)与构造函数语义
- 02.04.cpp
- 2.5 拷贝构造函数语义与移动构造函数语义
- 2.5.1 拷贝构造函数语义
- 2.5.2 移动构造函数语义
- 02.05.cpp
- 2.6 程序转换语义
- 2.6.1 定义时初始化对象
- 2.6.2 参数的初始化
- 2.6.3 返回值初始化
- 02.06.cpp
- 2.7 程序的优化
- 2.7.1 开发者层面优化
- 2.7.2 编译器层面优化
- 02.07.cpp
- 2.8 程序优化续、拷贝构造函数续与深浅拷贝
- 2.8.1 程序优化续
- 2.8.2 拷贝构造续
- 2.8.3 深浅拷贝
- 02.08.cpp
- 2.9 成员初始化列表
- 2.9.1 何时必须用成员初始化列表
- 2.9.2 使用初始化列表的优势
- 2.9.3 初始化列表的细节探究
- 02.09.cpp
2.1 类对象(实例)占用空间
对象有地址,地址单元里面保存有内容。
即便sizeof(空类)或者size(空类对象),得到的结果是1,不是0。
类对象至少占用1字节内存空间。
- 成员变量包含在每个对象中,占字节;
- 类中成员函数不占类对象内存空间,跟着类走。
02.01.cpp
#include <iostream>
using namespace std;struct A1
{
};class A2
{
public:void func(){};void func1(){};void func2(){};
};class A3
{
public:void func(){};void func1(){};void func2(){};char ab;
};class A4
{
public:void func(){};void func1(){};void func2(){};int ab;
};int main()
{A1 a;cout << sizeof(a) << endl;cout << sizeof(A1) << endl;cout << sizeof(A2) << endl;cout << sizeof(A3) << endl;cout << sizeof(A4) << endl;cout << "Over!\n";return 0;
}
2.2 对象结构的发展和演化
- 非静态成员变量跟着对象走
非静态成员变量保存在对象内部,占据对象的内存空间。
class A {int a = 100;
};A a;
cout <<sizeof(a) <<endl; //4
cout <<sizeof(A) <<endl; //4
- 静态成员变量跟对象没有关系
静态成员变量保存在类对象外面。
class A {static int a;static int b;
};A a;
cout <<sizeof(a) <<endl; //1
cout <<sizeof(A) <<endl; //1
#include <iostream>
using namespace std;class A {
public:static int a;static int b; //static int b = 5;错误static const int c;const int d = 10; //必须赋值static const int f = 22;
};int A::a = 25;
const int A::c = 25; //static不能添加
//const int A::f = 25; //errorint main() {cout <<sizeof(A) <<endl;; //4,const int dA a;cout <<sizeof(a) <<endl; //4return 0;
}
- 成员函数
无论静态还是非静态,都保存在类对象之外。
class A {static void sfunc(){};void func(){};
};A a;
cout <<sizeof(a) <<endl; //1
cout <<sizeof(A) <<endl; //1
- 虚函数
虚函数表(virtual table,vtbl),放置一堆指向虚函数的指针。
类中存在虚函数(一或多)时,类对象会增加4(8字节,64位测试)字节,用于存放虚函数表指针(virtual table pointer,vptr,二级指针)。
系统内部,会在构造函数、析构函数、拷贝赋值运算符等里面,为vptr设值及重置。
class A {virtual void f1() {};virtual void f2() {};
};A a;
cout <<sizeof(a) <<endl; //8
cout <<sizeof(A) <<endl; //8
总结:
- 静态成员变量不计算在对象的sizeof内。
- 普通成员函数和静态成员函数不计算在对象的sizeof内。
- 虚函数不计算在对象的sizeof内,但虚函数会让对象增加4或8字节以容纳虚函数表指针。
- 虚函数表基于类,跟着类走。
- 类中多个成员变量,存在内存字节对齐问题。
- 所有类型指针,内存大小固定(4或8字节)。
cout <<sizeof(char *) <<endl; //8
cout <<sizeof(int *) <<endl; //8
class A {public:A(){};virtual ~A(){};float getvalue() const {return m_value;}static int s_getcount() {return ms_scount;}virtual void vfrandfunc() {};
protected:float m_value;static int ms_scount;
};cout <<sizeof(A) <<endl; //16A a;
cout <<sizeof(a) <<endl; //16
类对象大小组成(sizeof):
- 非静态成员变量占据内存总量和这些成员变量间内存字节对齐所额外占用的内存。
- 若有虚函数,会产生虚函数表指针vptr(4或8字节)。
02.02.cpp
#include <iostream>
using namespace std;class A1
{int a = 100;
};
class A2
{static int a;static int b;
};
class A3
{static void sfunc(){};void myfunc(){};
};
class A4
{virtual void myfunc3(){};virtual void myfunc4(){};
};class myobject
{
public:myobject(){}; // 构造函数virtual ~myobject(){}; // 析构函数float getvalue() const { return m_value; } // 普通成员函数static int s_getcount() { return ms_scount; } // 静态成员函数virtual void vfrandfunc(){}; // 虚函数protected:float m_value; // 普通成员变量static int ms_scount; // 静态成员变量
};int main()
{cout << sizeof(A1) << endl; // 4cout << sizeof(A2) << endl; // 1cout << sizeof(A3) << endl; // 1cout << sizeof(A4) << endl; // 8cout << sizeof(char *) << endl; // 8cout << sizeof(int *) << endl; // 8cout << sizeof(myobject) << endl; // 16cout << "Over!\n";return 0;
}
2.3 this指针调整
this指针调整一般存在于多重继承情况下。
#include <iostream>
using namespace std;class A {
public:A(){cout <<"A::A(), this = " <<this <<endl;}void funcA() {cout <<"A::funcA(), this = " <<this <<endl;}int a;
};class B {
public:B(){cout <<"B::B(), this = " <<this <<endl;}void funcB() {cout <<"B::funcB(), this = " <<this <<endl;}int b;
};class C : public A, public B {
public:C(){cout <<"C::C(), this = " <<this <<endl;}void funcC() {cout <<"C::funcC(), this = " <<this <<endl;}int c;
};int main() {cout <<sizeof(A) <<endl;cout <<sizeof(B) <<endl;cout <<sizeof(C) <<endl;C c;c.funcA();c.funcB(); //动态调整this地址c.funcC();return 0;
}
C类先继承A类,后继承B类。
int a;//0x3703dff654,A类和C类对象 this指针
int b;//0x3703dff658,B类对象 this指针
int c;
输出
4
4
12
A::A(), this = 0x3703dff654
B::B(), this = 0x3703dff658
C::C(), this = 0x3703dff654
A::funcA(), this = 0x3703dff654
B::funcB(), this = 0x3703dff658
C::funcC(), this = 0x3703dff654
调用哪个子类的成员函数,this等于该对象内存布局中子类对象的起始地址(编译器自动调整)。
02.03.cpp
#include <iostream>
using namespace std;class A
{
public:int a;A(){cout << "A::A(), this = " << this << endl;}void funA(){cout << "A::funcA(), this = " << this << endl;}
};class B
{
public:int b;B(){cout << "B::B(), this = " << this << endl;}void funB(){cout << "B::funcB(), this = " << this << endl;}
};class C : public A, public B
{
public:int c;C(){cout << "C::C(), this = " << this << endl;}void funC(){cout << "C::funcC(), this = " << this << endl;}void funB(){cout << "C::funcB(), this = " << this << endl;}
};int main()
{cout << sizeof(A) << endl; // 4cout << sizeof(B) << endl; // 4cout << sizeof(C) << endl; // 12 = 4 + 4 + 4C myc;myc.funA();myc.funB();myc.B::funB();myc.funC();return 0;
}//输出
/*
4
4
12
A::A(), this = 0xf2637ffb84
B::B(), this = 0xf2637ffb88
C::C(), this = 0xf2637ffb84
A::funcA(), this = 0xf2637ffb84
C::funcB(), this = 0xf2637ffb84
B::funcB(), this = 0xf2637ffb88
C::funcC(), this = 0xf2637ffb84
*/
2.4 分析obj(目标文件)与构造函数语义
默认构造函数(缺省构造函数),必要时,编译器才会合成默认构造函数。
.cpp文件编译生成.obj文件。
Developer Command Prompt
dumpbin /all *.obj > *.txt
objdump .obj
编译器合成默认构造函数的情况:
一、
(1)该类无任何构造函数。
(2)该类含有类类型的成员变量,且该类类型有一个默认构造函数。
合成默认构造函数中安插代码来调用类类型成员变量的默认构造函数。
class MATX
{
public:MATX(){cout << "goodHAHAHA" << endl;}
};class M0TX
{
public:M0TX(){cout << "Ogood!" << endl;}
};class MBTX
{
public:int m_i;int m_j;void funct(){cout << "Iamverygood" << endl;}public:MATX ma; // 类类型成员变量M0TX m0; // 定义顺序,决定调用构造函数的顺序
};
二、
(1)该类无任何构造函数。
(2)该类有父类,且父类有默认构造函数。
合成默认构造函数中安插代码来调用父类的默认构造函数。
class MBTXPARENT
{
public:MBTXPARENT(){cout << "MBTXPARENT()" << endl;}
};class MBTX2 : public MBTXPARENT
{
public:int m_i;int m_j;void funct(){cout << "Iamverygood" << endl;}
};
三、
(1)该类有虚函数。
合成默认构造函数中安插代码来将类的虚函数表地址赋给该类对象的虚函数表指针。
class MBTX3
{
public:int m_i;int m_j;void funct(){cout << "Iamverygood" << endl;}virtual void mvirfunc() // 虚函数{cout << "mvirfunc" << endl;}
};
class MBTXPARENT4
{
public:MBTXPARENT4(){cout << "MBTXPARENT4()" << endl;}
};
class MBTX4 : public MBTXPARENT4
{
public:int m_i;int m_j;MBTX4() // 缺省构造函数{m_i = 15;// 编译器会在内部插入代码// 调用父类的构造函数// 虚函数表指针赋值}void funct(){cout << "Iamverygood" << endl;}virtual void mvirfunc() // 虚函数{cout << "mvirfunc" << endl;}
};
四、
(1)该类有虚函数。
合成默认构造函数中安插代码来给虚基类表指针赋值以及调用父类的构造函数。
class Grand // 爷爷类
{
public:
};
class A : virtual public Grand // 注意virtual
{
public:
};
class A2 : virtual public Grand // 注意virtual
{
public:
};
class C : public A, public A2 // 这里不需要virtual
{
public:C(){cout << "C::C()" << endl;}
};
五、
(1)定义成员变量时赋初值。
合成默认构造函数中安插代码来给成员变量赋初值。
class Time
{
public:Time(){cout << "Time::Time()" << endl;}int Second{10};
};
02.04.cpp
#include <iostream>
using namespace std;class MATX
{
public:MATX(){cout << "goodHAHAHA" << endl;}
};class M0TX
{
public:M0TX(){cout << "Ogood!" << endl;}
};class MBTX
{
public:int m_i;int m_j;void funct(){cout << "Iamverygood" << endl;}public:MATX ma; // 类类型成员变量M0TX m0; // 定义顺序,决定调用构造函数的顺序
};class MBTXPARENT2
{
public:MBTXPARENT2(){cout << "MBTXPARENT2()" << endl;}
};class MBTX2 : public MBTXPARENT2
{
public:int m_i;int m_j;void funct(){cout << "Iamverygood" << endl;}
};class MBTX3
{
public:int m_i;int m_j;void funct(){cout << "Iamverygood" << endl;}virtual void mvirfunc() // 虚函数{cout << "mvirfunc" << endl;}
};class MBTXPARENT4
{
public:MBTXPARENT4(){cout << "MBTXPARENT4()" << endl;}
};
class MBTX4 : public MBTXPARENT4
{
public:int m_i;int m_j;MBTX4() // 缺省构造函数{m_i = 15;// 编译器会在内部插入代码// 调用父类的构造函数// 虚函数表指针赋值}void funct(){cout << "Iamverygood" << endl;}virtual void mvirfunc() // 虚函数{cout << "mvirfunc" << endl;}
};class Grand // 爷爷类
{
public:
};
class A : virtual public Grand // 注意virtual
{
public:
};
class A2 : virtual public Grand // 注意virtual
{
public:
};
class C : public A, public A2 // 这里不需要virtual
{
public:C(){cout << "C::C()" << endl;}
};class Time
{
public:Time(){cout << "Time::Time()" << endl;}int Second{10};
};int main()
{MBTX myb;C cc; // 生成C类对象Time mytime;cout << "Over!\n";return 0;
}
2.5 拷贝构造函数语义与移动构造函数语义
类未定义拷贝构造函数,编译器必要时会合成拷贝构造函数。
2.5.1 拷贝构造函数语义
编译器合成拷贝构造函数的情况:
一、
(1)该类无任何拷贝构造函数。
(2)该类含有类类型的成员变量,且该类类型含有拷贝构造函数。
合成拷贝构造函数中安插代码来调用类类型成员变量的拷贝构造函数。
class CTB
{
public:CTB(const CTB &){cout << "CTB::CTB(const CTB &) " << endl;}CTB(){}
};
class A3
{
public:CTB m_ctb;
};
二、
(1)该类无任何拷贝构造函数。
(2)该类有父类,且父类含有拷贝构造函数。
合成拷贝构造函数中安插代码来调用父类的拷贝构造函数。
class CTB
{
public:CTB(const CTB &){cout << "CTB::CTB(const CTB &) " << endl;}CTB(){}
};
class CTBSon : public CTB
{
public:
};
三、
(1)该类无任何拷贝构造函数。
(2)该类或父类定义了虚函数。
合成拷贝构造函数中安插代码来给虚函数表指针赋值。
class CTBSon2
{
public:virtual void mvirfunc() {}
};
class CTB3
{
public:virtual void mvirfunc() {}
};
class CTBSon3 : public CTB3
{
public:
};
四、
(1)该类无任何拷贝构造函数。
(2)该类含有虚基类。
合成拷贝构造函数中安插代码来虚基类表赋值。
class Grand // 爷爷类
{
public:
};
class A : virtual public Grand
{
public:
};
class A2 : virtual public Grand
{
public:
};
class C : public A, public A2
{
public:
};
2.5.2 移动构造函数语义
编译器合成移动构造函数的情况:
一、类定义了拷贝构造函数、拷贝赋值运算符或者析构函数,编译器就不会合成移动构造函数。
二、类未定义了拷贝构造函数、拷贝赋值运算符和析构函数,且非静态成员都可以移动,才会合成。
可移动成员:
- 内置类型(int,float等)。
- 类类型含有移动操作相关函数,则该类型变量可移动。
struct TC
{int i; // 内置类型可以移动std::string s; // string类型定义了自己的移动操作
};TC a;a.i = 100;a.s = "I Love China!";const char *p = a.s.c_str();TC b = std::move(a); // 导致结构/类TC移动构造函数的执行,数据移动不是std::move所为,而是string的移动构造函数所为const char *q = b.s.c_str();
02.05.cpp
#include <iostream>
using namespace std;class A1
{
public:int m_test;
};class ASon
{
public:int m_testson;
};
class A20
{
public:int m_test;ASon asubobj;
};class CTB
{
public:CTB(const CTB &){cout << "CTB::CTB(const CTB &) " << endl;}CTB(){}
};
class A3
{
public:CTB m_ctb;
};
class CTBSon : public CTB
{
public:
};class CTBSon2
{
public:virtual void mvirfunc() {}
};
class CTB3
{
public:virtual void mvirfunc() {}
};
class CTBSon3 : public CTB3
{
public:
};class Grand // 爷爷类
{
public:
};
class A : virtual public Grand
{
public:
};
class A2 : virtual public Grand
{
public:
};
class C : public A, public A2
{
public:
};struct TC
{int i; // 内置类型可以移动std::string s; // string类型定义了自己的移动操作
};int main()
{{A1 mya1;mya1.m_test = 15;A1 mya2 = mya1; // 按值拷贝,不需要合成拷贝构造函数}A20 mya1;mya1.m_test = 15;mya1.asubobj.m_testson = 120;A20 mya2 = mya1; // 直接复制数据的实现手法,递归式地去复制类的成员变量A3 a3;CTBSon myctbson1;CTBSon myctbson2 = myctbson1;C cc;C cc2 = cc;TC a;a.i = 100;a.s = "I Love China!";const char *p = a.s.c_str();TC b = std::move(a); // 导致结构/类TC移动构造函数的执行,数据移动不是std::move所为,而是string的移动构造函数所为const char *q = b.s.c_str();cout << "Over!\n";return 0;
}
2.6 程序转换语义
编译器如何拆分代码,进行解析。
2.6.1 定义时初始化对象
class X
{
public:int m_i;X(const X &tmpx){m_i = tmpx.m_i;cout << "X::X(const X &)" << endl;}X(){m_i = 0;cout << "X::X()" << endl;}~X(){cout << "X::~X()" << endl;}public:void functest(){cout << "X::functest()" << endl;}
};/*X x0; // X::X()x0.m_i = 15;X x3 = x0; // X::X(const X &)
*/
// //步骤一:定义一个对象,为对象分配内存,但这里编译器内部并没有调用构造函数。// //如果以编译器的眼光看X x3_2,则不会调用构造函数。// //除非编译器主动增加这种x3_2.X::X();这种代码行到现有的代码中// X x3_2;// //步骤二:直接调用对象的拷贝构造函数去了// x3_2.X::X(x0);
2.6.2 参数的初始化
void func1(X tmpx)
{
}X x0;//func1(x0);// 程序员视角(现代编译器视角)// X tmpx = x0;//编译器在func1函数空间内构造了tmpx对象,函数返回前析构掉。// 老编译器视角// X tmpobj; //编译器产生出来一个临时对象// tmpobj.X::X(x0); //调用拷贝构造函数// func(tmpobj); //用临时对象调用func// tmpobj.X::~X(); //func()被调用完成后,析构函数被调用// void func1(X &tmpx){} //函数参数也变成了引用
2.6.3 返回值初始化
class X
{
public:int m_i;X(const X &tmpx){m_i = tmpx.m_i;cout << "X::X(const X &)" << endl;}X(){m_i = 0;cout << "X::X()" << endl;}~X(){cout << "X::~X()" << endl;}public:void functest(){cout << "X::functest()" << endl;}
};void func1(X tmpx)
{
}X func2()
{X x0;return x0;
}{X my = func2();// 编译器视角// X my; //分配对象内存空间// void func2(X & my) // 传递引用参数//{// X x0; // 调用构造函数,编译器内部有诸如x0.X::X()这种代码// my.X::X(x0); // 调用拷贝构造函数//}}cout << "*****************" << endl;{func2().functest();// 编译器视角// X my; //分配对象内存空间,未调用构造函数// void func2(X & my) // 传递引用参数//{// X x0; // 调用构造函数,编译器内部有诸如x0.X::X()这种代码// my.X::X(x0); // 调用拷贝构造函数//}// my.functest();// my.X::~X();// 函数执行完后,调用析构函数}cout << "*****************" << endl;{X (*pf)(); // 定义函数指针pf = func2;pf().functest();// 编译器视角// void (*pf)(X &);// pf = func2;// X my;// pf(my); // 传递引用,内部调用拷贝构造函数// my.functest();// my.X::~X();}
02.06.cpp
#include <iostream>
using namespace std;class X
{
public:int m_i;X(const X &tmpx){m_i = tmpx.m_i;cout << "X::X(const X &)" << endl;}X(){m_i = 0;cout << "X::X()" << endl;}~X(){cout << "X::~X()" << endl;}public:void functest(){cout << "X::functest()" << endl;}
};void func1(X tmpx)
{
}X func2()
{X x0;return x0;
}int main()
{{X x0; // X::X()x0.m_i = 15;X x1 = x0; // X::X(const X &)X x2(x0); // X::X(const X &)X x3 = x0; // X::X(const X &)// X::~X()// X::~X()// X::~X()// X::~X()// //步骤一:定义一个对象,为对象分配内存,但这里编译器内部并没有调用构造函数。// //如果以编译器的眼光看X x3_2,则不会调用构造函数。// //除非编译器主动增加这种x3_2.X::X();这种代码行到现有的代码中// X x3_2;// //步骤二:直接调用对象的拷贝构造函数去了// x3_2.X::X(x0);}cout << "*****************" << endl;{X x0;func1(x0);// 程序员视角(现代编译器视角)// X tmpx = x0;//编译器在func1函数空间内构造了tmpx对象,函数返回前析构掉。// 老编译器视角// X tmpobj; //编译器产生出来一个临时对象// tmpobj.X::X(x0); //调用拷贝构造函数// func(tmpobj); //用临时对象调用func// tmpobj.X::~X(); //func()被调用完成后,析构函数被调用// void func1(X &tmpx){} //函数参数也变成了引用}cout << "*****************" << endl;{X my = func2();// 编译器视角// X my; //分配对象内存空间// void func2(X & my) // 传递引用参数//{// X x0; // 调用构造函数,编译器内部有诸如x0.X::X()这种代码// my.X::X(x0); // 调用拷贝构造函数//}}cout << "*****************" << endl;{func2().functest();// 编译器视角// X my; //分配对象内存空间,未调用构造函数// void func2(X & my) // 传递引用参数//{// X x0; // 调用构造函数,编译器内部有诸如x0.X::X()这种代码// my.X::X(x0); // 调用拷贝构造函数//}// my.functest();// my.X::~X();// 函数执行完后,调用析构函数}cout << "*****************" << endl;{X (*pf)(); // 定义函数指针pf = func2;pf().functest();// 编译器视角// void (*pf)(X &);// pf = func2;// X my;// pf(my); // 传递引用,内部调用拷贝构造函数// my.functest();// my.X::~X();}cout << "Over!\n";return 0;
}
2.7 程序的优化
2.7.1 开发者层面优化
struct CTempValue
{int val1;int val2;CTempValue(int v1 = 0, int v2 = 0): val1(v1), val2(v2){cout << "CTempValue::CTempValue(int, int)" << endl;cout << "val1 = " << val1 << endl;cout << "val2 = " << val2 << endl;}CTempValue(const CTempValue &t): val1(t.val1), val2(t.val2){cout << "CTempValue::CTempValue(const CTempValue &)" << endl;};virtual ~CTempValue(){cout << "CTempValue::~CTempValue()" << endl;};
};CTempValue Double1(const CTempValue &ts)
{CTempValue tmpm; // 构造+析构函数tmpm.val1 = ts.val1 * 2;tmpm.val2 = ts.val2 * 2;return tmpm; // 拷贝构造+析构
}CTempValue Double2(const CTempValue &ts)
{return CTempValue(ts.val1 * 2, ts.val2 * 2); // 构造+析构
}// 编译器视角
// CTempValue& tmpobj; //分配内存空间
// void Double2(CTempValue& tmpobj, const CTempValue& ts) //插入一个tmpobj引用参数
// {
// tmpobj.CTempValue::CTempValue(ts.val1 * 2, ts.val2 * 2);//编译器手工调用构造函数
// }
// tmpobj.CTempValue::~CTempValue();//析构
2.7.2 编译器层面优化
针对返回临时对象的情况进行的优化,NRV(Named Return Value)优化或者RVO(Return Value Optimization)
g++ -fno-elide-constructors,关闭优化
Visual Studio优化选项
配置属性->C/C+±>优化
配置属性->C/C+±>代码生成->运行时基本检查
02.07.cpp
#include <iostream>
#include <time.h>
using namespace std;struct CTempValue
{int val1;int val2;CTempValue(int v1 = 0, int v2 = 0): val1(v1), val2(v2){cout << "CTempValue::CTempValue(int, int)" << endl;cout << "val1 = " << val1 << endl;cout << "val2 = " << val2 << endl;}CTempValue(const CTempValue &t): val1(t.val1), val2(t.val2){cout << "CTempValue::CTempValue(const CTempValue &)" << endl;}virtual ~CTempValue(){cout << "CTempValue::~CTempValue()" << endl;}
};CTempValue Double1(const CTempValue &ts)
{CTempValue tmpm; // 构造+析构函数tmpm.val1 = ts.val1 * 2;tmpm.val2 = ts.val2 * 2;return tmpm; // 拷贝构造+析构
}CTempValue Double2(const CTempValue &ts)
{return CTempValue(ts.val1 * 2, ts.val2 * 2); // 构造+析构
}// 编译器视角
// CTempValue& tmpobj; //分配内存空间
// void Double2(CTempValue& tmpobj, const CTempValue& ts) //插入一个tmpobj引用参数
// {
// tmpobj.CTempValue::CTempValue(ts.val1 * 2, ts.val2 * 2);//编译器手工调用构造函数
// }
// tmpobj.CTempValue::~CTempValue();//析构struct CTempValue2
{int val1;int val2;CTempValue2(int v1 = 0, int v2 = 0): val1(v1), val2(v2) {}CTempValue2(const CTempValue &t): val1(t.val1), val2(t.val2) {}virtual ~CTempValue2() {}
};CTempValue2 Double(const CTempValue2 &ts)
{CTempValue2 tmpm; // 构造+析构函数tmpm.val1 = ts.val1 * 2;tmpm.val2 = ts.val2 * 2;return tmpm; // 拷贝构造+析构
}int main()
{if (0){CTempValue ts1(10, 20);Double1(ts1);CTempValue ts2 = Double2(ts1);}CTempValue2 ts(10, 20);clock_t start = clock(); // 程序开始到本行执行时所用的毫秒数cout << "start = " << start << endl;for (int i = 0; i < 1000000; i++)Double(ts);clock_t end = clock();cout << "end = " << end << endl;cout << "end - start = " << end - start << endl;cout << "Over!\n";return 0;
}//g++ -fno-elide-constructors 02.07.cpp -o 02.07 && 02.07
2.8 程序优化续、拷贝构造函数续与深浅拷贝
2.8.1 程序优化续
struct X2
{int m_i;X2(const X1 &tmpx){m_i = tmpx.m_i;cout << "X2::X2(const X1 &)" << endl;}X2(){m_i = 0;cout << "X2::X2()" << endl;}~X2(){cout << "X2::~X2()" << endl;}explicit X2(int value) : m_i(value) // 类型转换构造函数(带有一个形参的拷贝构造函数){cout << "X2::X2(int)" << endl;}
};if (0){cout << "----begin----" << endl;X2 x10(1000);cout << "--------" << endl;//X2 x11 = 1000;//error//cout << "--------" << endl;X2 x12 = X2(1000);cout << "--------" << endl;X2 x13 = (X2)1000;cout << "----end----" << endl;}//{// //编译器视角// X x10; //编译器视角是不调用构造函数的// x10.X::X(1000);//}//{// //编译器视角// X _tmp0; //编译器生成的临时对象// _tmp0.X::X(1000); //带一个参数的构造函数被调用// X x12;// x12.X::X(_tmp0); //拷贝构造函数被调用// _tmp0.X::~X(); //调用析构//}
2.8.2 拷贝构造续
只是简单数据类型的成员变量,int、double等时,不需要拷贝构造函数,编译器内部本身支持成员变量的bitwise(按位)复制。
增加拷贝构造函数会导致编译器内置的bitwise(按位)复制失效,需要自己负责成员变量的初始化工作。
2.8.3 深浅拷贝
只复制指针值,指针指向的地址为做额外处理,叫浅拷贝。
复制指针值和指针指向的地址内容,叫深拷贝。
02.08.cpp
#include <iostream>
#include <time.h>
using namespace std;struct X1
{int m_i;X1(const X1 &tmpx){m_i = tmpx.m_i;cout << "X1::X1(const X1 &)" << endl;}X1(){m_i = 0;cout << "X1::X1()" << endl;}~X1(){cout << "X1::~X1()" << endl;}// explicit X(int value) :m_i(value)X1(int value) : m_i(value) // 类型转换构造函数(带有一个形参的拷贝构造函数){cout << "X1::X1(int)" << endl;}
};struct X2
{int m_i;X2(const X1 &tmpx){m_i = tmpx.m_i;cout << "X2::X2(const X1 &)" << endl;}X2(){m_i = 0;cout << "X2::X2()" << endl;}~X2(){cout << "X2::~X2()" << endl;}explicit X2(int value) : m_i(value) // 类型转换构造函数(带有一个形参的拷贝构造函数){cout << "X2::X2(int)" << endl;}
};struct X
{int m_i;int *p_mi;X(const X &tmpx){m_i = tmpx.m_i;p_mi = new int(100); // 为指针类型成员变量单独分配内存memcpy(p_mi, tmpx.p_mi, sizeof(int));cout << "X::X(const X &)" << endl;}X(){m_i = 0;p_mi = new int(100);cout << "X::X()" << endl;}~X(){delete p_mi;cout << "X::~X()" << endl;}explicit X(int value): m_i(value) // 类型转换构造函数(带有一个形参的拷贝构造函数){p_mi = new int(100);cout << "X::X(int)" << endl;}
};int main()
{if (0){cout << "----begin----" << endl;X1 x10(1000);cout << "--------" << endl;X1 x11 = 1000;cout << "--------" << endl;X1 x12 = X1(1000);cout << "--------" << endl;X1 x13 = (X1)1000;cout << "----end----" << endl;}if (0){cout << "----begin----" << endl;X2 x10(1000);cout << "--------" << endl;// X2 x11 = 1000;//error// cout << "--------" << endl;X2 x12 = X2(1000);cout << "--------" << endl;X2 x13 = (X2)1000;cout << "----end----" << endl;}//{// //编译器视角// X x10; //编译器视角是不调用构造函数的// x10.X::X(1000);//}//{// //编译器视角// X _tmp0; //编译器生成的临时对象// _tmp0.X::X(1000); //带一个参数的构造函数被调用// X x12;// x12.X::X(_tmp0); //拷贝构造函数被调用// _tmp0.X::~X(); //调用析构//}{X x0;x0.m_i = 150;X x1(x0);cout << x1.m_i << endl;}cout << "Over!\n";return 0;
}// g++ -fno-elide-constructors 02.08.cpp -o 02.08 && 02.08
2.9 成员初始化列表
2.9.1 何时必须用成员初始化列表
(1)成员变量是引用类型
struct A1
{int &m_yy;A1(int &tmpvalue) : m_yy(tmpvalue){// m_yy = tmpvalue;m_yy = 160;}
};int abc = 1;A1 a1(abc);cout << abc << endl;
(2)成员变量是const类型
struct A2
{const int &m_yy;A2(const int &tmpvalue) : m_yy(tmpvalue){}
};A2 a2(22);
(3)继承基类,基类的构造函数有参数
struct Base3
{int ba;int bb;Base3(int tmpa, int tmpb): ba(tmpa), bb(tmpb){}
};
struct A3 : Base3
{int m_x;int m_y;int &m_yy;const int m_myc;A3(int &tmpvalue): m_yy(tmpvalue), m_myc(tmpvalue), Base3(tmpvalue, tmpvalue){m_x = 0;m_y = 0;m_yy = 163; // 就等于修改外界的abc变量值}
};int abc3 = 1;A3 a3(abc3);cout << abc3 << endl;
(4)成员变量有类类型,该类类型的构造函数有参数
struct CSub
{CSub(int tmpvalue) {}
};
struct A4
{CSub cmysub;A4(int tmpvalue) : cmysub(tmpvalue){}
};A4 a4(3);
2.9.2 使用初始化列表的优势
提高程序运行效率。
struct X
{int m_i;X(int value = 0) : m_i(value){printf("this = %p", this);cout << "X::X(int)" << endl;}X(const X &tmpv){printf("this = %p", this);cout << "X::X(const X &)" << endl;}X &operator=(const X &tmpTime){printf("this = %p", this);cout << "X::X &operator=(const X &)" << endl;return *this; // 返回一个该对象的引用}~X(){printf("this = %p", this);cout << "X::~X()" << endl;}
};struct XA
{X xobj;//构造函数int m_test;XA(int tmpvalue){xobj = 1000;//构造函数+拷贝构造函数m_test = 500;}
};struct XA2
{X xobj;int m_test;XA2(int tmpvalue):xobj(1000)//构造函数{m_test = 500;}
};
类类型成员变量的初始化,放在构造函数的初始化列表中进行初始化比放在构造函数函数体中初始化效率更高。
2.9.3 初始化列表的细节探究
(1)初始化列表中的代码被编译器安插在构造函数中;
(2)初始化列表中的代码在构造函数的函数体代码执行前被执行。
(3)初始化列表中成员变量的初始化顺序根据类中定义的顺序。
02.09.cpp
#include <iostream>
#include <stdio.h>
using namespace std;struct A1
{int &m_yy;A1(int &tmpvalue) : m_yy(tmpvalue){// m_yy = tmpvalue;m_yy = 160;}
};struct A2
{const int &m_yy;A2(const int &tmpvalue) : m_yy(tmpvalue){}
};struct Base3
{int ba;int bb;Base3(int tmpa, int tmpb): ba(tmpa), bb(tmpb){}
};
struct A3 : Base3
{int m_x;int m_y;int &m_yy;const int m_myc;A3(int &tmpvalue): m_yy(tmpvalue), m_myc(tmpvalue), Base3(tmpvalue, tmpvalue){m_x = 0;m_y = 0;m_yy = 163; // 就等于修改外界的abc变量值}
};struct CSub
{CSub(int tmpvalue) {}
};
struct A4
{CSub cmysub;A4(int tmpvalue) : cmysub(tmpvalue){}
};struct X
{int m_i;X(int value = 0) : m_i(value){printf("this = %p", this);cout << "X::X(int)" << endl;}X(const X &tmpv){printf("this = %p", this);cout << "X::X(const X &)" << endl;}X &operator=(const X &tmpTime){printf("this = %p", this);cout << "X::X &operator=(const X &)" << endl;return *this; // 返回一个该对象的引用}~X(){printf("this = %p", this);cout << "X::~X()" << endl;}
};struct XA
{X xobj;int m_test;XA(int tmpvalue){xobj = 1000;m_test = 500;}
};struct XA2
{X xobj;int m_test;int m_test2;XA2(int tmpvalue): xobj(1000), m_test2(m_test), m_test(500){m_test = 500;}
};int main()
{int abc = 1;A1 a1(abc);cout << abc << endl;A2 a2(22);int abc3 = 1;A3 a3(abc3);cout << abc3 << endl;A4 a4(3);{XA2 myaobj(1000);}cout << "Over!\n";return 0;
}