《C++新经典对象模型》之第2章 对象

news/2024/7/27 8:01:11/文章来源:https://blog.csdn.net/oqqyx1234567/article/details/124950654

《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 对象结构的发展和演化

  1. 非静态成员变量跟着对象走
    非静态成员变量保存在对象内部,占据对象的内存空间。
class A {int a = 100;
};A a;
cout <<sizeof(a) <<endl; //4
cout <<sizeof(A) <<endl; //4
  1. 静态成员变量跟对象没有关系
    静态成员变量保存在类对象外面。
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;
}
  1. 成员函数
    无论静态还是非静态,都保存在类对象之外。
class A {static void sfunc(){};void func(){};
};A a;
cout <<sizeof(a) <<endl; //1
cout <<sizeof(A) <<endl; //1
  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 移动构造函数语义

编译器合成移动构造函数的情况:
一、类定义了拷贝构造函数、拷贝赋值运算符或者析构函数,编译器就不会合成移动构造函数。
二、类未定义了拷贝构造函数、拷贝赋值运算符和析构函数,且非静态成员都可以移动,才会合成。
可移动成员:

  1. 内置类型(int,float等)。
  2. 类类型含有移动操作相关函数,则该类型变量可移动。
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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_996082.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FPGA——三速自适应以太网设计(1)基本模块

FPGA——以太网设计&#xff08;1&#xff09;基本模块 1. 协议解析&#xff08;1&#xff09;MAC层&#xff08;2&#xff09;IP层 和 ARP层&#xff08;3&#xff09;UDP层 和 ICMP层 2.1 MAC接收模块2.2 MAC发送模块3.1 IP接收模块3.2 IP发送模块4.1 UDP接收模块4.2 UDP发送…

以创新筑牢安全盾牌,广师大隐盾科技照亮软件知识产权保护之路

“很感谢隐盾科技团队的各位成员对我司计算机软件代码保护的鼎力相助……”广州市硬科技百强企业在给予隐盾科技团队的感谢信中写道。据了解&#xff0c;该公司在使用了隐盾科技团队研发的隐盾代码虚拟化系统后&#xff0c;企业开发盗版率从45%降至0%、保护该企业年侵权成本超过…

计算机设计大赛 深度学习的智能中文对话问答机器人

文章目录 0 简介1 项目架构2 项目的主要过程2.1 数据清洗、预处理2.2 分桶2.3 训练 3 项目的整体结构4 重要的API4.1 LSTM cells部分&#xff1a;4.2 损失函数&#xff1a;4.3 搭建seq2seq框架&#xff1a;4.4 测试部分&#xff1a;4.5 评价NLP测试效果&#xff1a;4.6 梯度截断…

Linux conntrack和iptables技术解析

Linux虚拟文件系统管理技术 1. netfilter解析1.1 netfilter的基础原理1.2 netfilter的相关hook 2. conntrack解析2.1 conntrack的基础原理2.2 conntrack的表记录解析 3. iptables解析3.1 iptables基础原理3.2 融合conntrack表的iptables规则 4. 疑问和思考4.1 conntrack和iptab…

Tensorflow2.0笔记 - 常见激活函数sigmoid,tanh和relu

本笔记主要记录常见的三个激活函数sigmoid&#xff0c;tanh和relu&#xff0c;关于激活函数详细的描述&#xff0c;可以参考这里&#xff1a; 详解激活函数&#xff08;Sigmoid/Tanh/ReLU/Leaky ReLu等&#xff09; - 知乎 import tensorflow as tf import numpy as nptf.__ve…

20240306-1-大数据的几个面试题目

面试题目 1. 相同URL 题目: 给定a、b两个文件&#xff0c;各存放50亿个url&#xff0c;每个url各占64字节&#xff0c;内存限制是4G&#xff0c;让你找出a、b文件共同的url&#xff1f; 方案1&#xff1a;估计每个文件的大小为50G64320G&#xff0c;远远大于内存限制的4G。所以…

【golang】26、retry-go 使用示例和源码解析

文章目录 一、使用方法1.1 http 示例1.1.1 retry.Do1.1.2 retry.DoWithData1.1.3 OnRetry1.1.4 根据 error 的类型&#xff0c;决定 delay 的时长1.1.5 自定义 retry function 二、API2.1 Do 执行2.1.1 Do2.1.2 DoWithData 2.2 Delay 策略2.3 错误处理2.3.1 Unwrap2.3.2 Unwrap…

口碑营销:品牌如何维护良好口碑?

企业的品牌传播最有效的方式莫过用户的口碑&#xff0c;互联网的发展为企业的品牌传播引入了驱动力&#xff0c;愈来愈多的企业花费更多的资源开展网络口碑的建设和维护&#xff0c;那么企业如何维护好网络口碑&#xff1f; 1、持续传递优质的品牌内容 内容是营销推广的支撑点&…

学习Java的第一天

一、Java简介 Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 面向对象程序设计语言和 Java 平台的总称。由 James Gosling和同事们共同研发&#xff0c;并在 1995 年正式推出。 后来 Sun 公司被 Oracle &#xff08;甲骨文&#xff09;公司收购&#xff0c;Jav…

如何做代币分析:以 LDO 币为例

作者&#xff1a;lesleyfootprint.network 编译&#xff1a;mingfootprint.network 数据源&#xff1a;LDO 代币仪表板 &#xff08;仅包括以太坊数据&#xff09; 在加密货币和数字资产领域&#xff0c;代币分析起着至关重要的作用。代币分析指的是深入研究与代币相关的数据…

【EI会议征稿通知】第四届人工智能,大数据与算法国际学术会议 (CAIBDA 2024)

第四届人工智能&#xff0c;大数据与算法国际学术会议 (CAIBDA 2024) 2024 4th International Conference on Artificial Intelligence, Big Data and Algorithms 由河南省科学院、河南大学主办&#xff0c;河南省科学院智慧创制研究所、河南大学学术发展部、河南大学人工智能…

Java 数据结构之链表

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if (headA null || headB null) return null;ListNode pA headA, pB headB;while (pA ! pB) {pA pA null ? headB : pA.next;pB pB null ? headA : pB.next;}return pA;} public ListNode rev…

【C++】String常用的函数总结

目录 一、string的构造函数方式&#xff1a; 二、常用的大小/容量相关操作&#xff1a; 三、string的常用修改操作&#xff1a; 四、string的遍历&#xff1a; 五、string的任意位置插入 / 删除&#xff1a; 六&#xff1a;补充&#xff1a; 一、string的构造函数方式&a…

HarmonyOS应用开发-环境搭建(windows环境)

官网地址&#xff1a;链接 DevEco Studio 3.1.1 Release&#xff1a;下载地址 1、安装DevEco Studio 直接安装即可 2、配置开发环境 1.运行已安装的DevEco Studio&#xff0c;首次使用&#xff0c;请选择Do not import settings&#xff0c;单击OK。 2.安装Node.js与ohpm。注…

umi4 项目使用 keepalive 缓存页面(umi-plugin-keep-alive、react-activation)

umi4使用keepalive 配置文件config\config.ts export default defineConfig({plugins: [umi-plugin-keep-alive], });安装add umi-plugin-keep-alive yarn add umi-plugin-keep-alive页面 A import { KeepAlive, history, useAliveController } from umijs/max; const Page…

数组常见算法

一、数组排序 冒泡排序 本篇我们介绍最基本的排序方法&#xff1a;冒泡排序。 实现步骤 1、比较两个相邻元素&#xff0c;如果第一个比第二个大&#xff0c;就交换位置 2、对每一对相邻元素进行同样的操作&#xff0c;除了最后一个元素 特点 每一轮循环后都会把最大的一个…

【设计模式】(二)设计模式六大设计原则

一、 设计原则概述 设计模式中主要有六大设计原则&#xff0c;简称为SOLID &#xff0c;是由于各个原则的首字母简称合并的来(两个L算一个,solid 稳定的)&#xff0c;六大设计原则分别如下&#xff1a; ​ 1、单一职责原则&#xff08;Single Responsibitity Principle&#…

【李沐精读系列】BERT精读

论文&#xff1a;BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 参考&#xff1a;BERT论文逐段精读、李沐精读系列、李宏毅版BERT讲解 一、介绍 BERT(Bidirectional EncoderRepresentation Transformer&#xff0c;双向Transformer编码器…

智能便捷|AIRIOT智慧充电桩管理解决方案

现如今随着对可持续交通的需求不断增加&#xff0c;电动车市场正在迅速扩大&#xff0c;建设更多更智能的充电桩&#xff0c;并通过管理平台提高充电设施的可用性和效率成为一项重要任务。传统的充电桩管理平台在对充电设施进行管理过程中&#xff0c;存在如下痛点&#xff1a;…

LeetCode每日一题 环形链表(链表)

题目描述 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&…