C++对象的构造和析构

    技术2022-07-15  44

    对象的构造和析构

    构造函数析构函数拷贝构造函数构造函数的分类使用拷贝构造函数的几种场合默认构造 析构函数构造函数规则浅拷贝 深拷贝构造函数初始化列表

    构造函数

    C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做 构造函数.  在对象被创建的时候,用来初始化对象的函数

    自动调用:一般情况下C++编译器会自动调用构造函数. 手动调用:在一些情况下则需要手工调用构造函数. 

    析构函数

    C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做 析构函数.

    析构函数的作用,并不是删除对象,而在对象销毁前完成的一些清理工作。 析构函数调用的顺序,跟构造函数相反,先构造的,后析构

    拷贝构造函数

    由己存在的对象,创建新对象。也就是说新对象,不由构造器来构造,而是由 拷贝  构造器来完成。拷贝构造器的格式是固定的。

    构造函数的分类

    1.有参构造 2.无参构造 3.拷贝构造

    使用拷贝构造函数的几种场合

    #define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; class Test { public: Test() { cout << "test()..." << endl; m_x = 0; m_y = 0; } Test(int x, int y) { cout << "Test(int x, int y)..." << endl; m_x = x; m_y = y; } Test(const Test & another) { cout << "Test(const Test &)..." << endl; m_x = another.m_x; m_y = another.m_y; } void operator=(const Test &another) { cout << "operatoer = (const Test &)" << endl; m_x = another.m_x; m_y = another.m_y; } void printT() { cout << "x = " << m_x << ", m_y = " << m_y << endl; } ~Test() { cout << "~Test()..." << endl; } private: int m_x; int m_y; }; //析构函数调用的顺序, 跟构造相反, 谁先构造的,谁后析构。 //场景1 对象t1初始化对象t2 void test1() { Test t1(10, 20); Test t2(t1);//Test t2 = t1; } //场景2 =操作符 void test2() { Test t1(10, 20); Test t2; t2 = t1; //调用的不是拷贝构造函数,而是赋值=操作符函数 } //场景3 实参t1初始化形参t void func(Test t)//Test t = t1; //Test t 的拷贝构造函数 { cout << "func begin..." << endl; t.printT(); cout << "func end..." << endl; } void test3() { cout << "test3 begin..." << endl; Test t1(10, 20); func(t1); cout << "test3 end..." << endl; } //场景4 匿名对象 Test func2() { cout << "func2 begin..." << endl; Test temp(10, 20); temp.printT(); cout << "func2 end..." << endl; return temp; }//匿名的对象 = temp 匿名对象.拷贝构造(temp) void test4() { cout << "test4 being.. " << endl; func2();// 返回一个匿名对象。 当一个函数返回一个匿名对象的时候,函数外部没有任何 //变量去接收它, 这个匿名对象将不会再被使用,(找不到), 编译会直接将个这个匿名对象 //回收掉,而不是等待整改函数执行完毕再回收. //匿名对象就被回收。 cout << "test4 end" << endl; } //场景5 匿名对象 初始化 (未初始化的)对象,匿名对象转正 void test5() { cout << "test 5begin.. " << endl; Test t1 = func2(); //会不会触发t1拷贝构造来 t1.拷贝(匿名)? //并不会触发t1拷贝,而是 将匿名对象转正 t1, //把这个匿名对象 起了名字就叫t1.j cout << "test 5 end.." << endl; } //场景6 匿名对象 赋值给 (已初始化的)对象,匿名对象被析构 void test6() { cout << "test6 begin..." << endl; Test t1;//t1已经被初始化了。 t1 = func2(); //t1已经被初始化了,所以func2返回的匿名对象不会再次转正,而依然是匿名对象。 //所以t1会调用等号操作符,t1.operator=(匿名对象), 然后编译器会立刻回收掉匿名对象 t1.printT(); cout << "test6 end.." << endl; } int main(void) { //test1(); //test2(); //test3(); //test4(); //test5(); test6(); return 0; }

    注意场景4,5,6

    有关 匿名对象的去和留 //如果用匿名对象 初始化 另外一个同类型的对象, 匿名对象 转成有名对象 //如果用匿名对象 赋值给 另外一个同类型的对象, 匿名对象 被析构

    默认构造 析构函数

    当类中没有 显示的无参,显示的有参,,编译器会默认提供一个无参构造函数,并且其函数体为空。 当类中没有 显示的拷贝构造,编译器会默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制。 当类中没有 显示的析构函数,编译器会默认提供一个析构函数。

    构造函数规则

    只要你写了构造函数,则必须要用

    浅拷贝 深拷贝

    系统提供默认的拷贝构造器,一经定义不再提供。但系统提供的默认拷贝 构造器是 等位拷贝,也就是通常意义上的浅拷贝。如果类中包含的数据元素全 部在栈上,浅拷贝 也可以满足需求的。但如果堆上的数据,则会发生多次析构行为。  

    //=操作符 void operator=(const Name &another) { cout << “operatoer = (const Name &)” << endl; m_p = another.m_p; }

    可以把 释放空间函数free() 放在析构函数里面,当程序结束时,自动调用,回收空间 或者自己写一个释放函数接口,最后自己再调用进行释放 void myFree(){ free(m_p); m_p = NULL; }

    构造函数初始化列表

     如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成 员它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进 行初始化,就必须调用这个类成员的带参数的构造函数,  如果没有初始化列表,那么他将无法完成第一步,就会报错。 

    #define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; class A { public: A(int a) { cout << "A()..."<<a << endl; m_a = a; } ~A() { cout << "~A()" << endl; } void printA() { cout << "a = " << m_a << endl; } private: int m_a; }; //构造函数的初始化列表 class B { public: B(A a1, A a2, int b) : m_a1(a1), m_a2(a2) { cout << "B(A&, A&, int)..." << endl; m_b = b; } //构造对象成员的顺序跟初始化列表的顺序无关 //而是跟成员对象的定义顺序有关 B(int a1, int a2, int b) : m_a1(a1), m_a2(a2) //前后赋值顺序无关 { cout << "B(int, int, int)..." << endl; m_b = b; } void printB() { cout << "b = " << m_b << endl; m_a1.printA(); m_a2.printA(); } ~B() { cout << "~B().." << endl; } private: //声明顺序有关 int m_b; A m_a2; A m_a1; }; void test1() { A a1(10), a2(100); B b(a1, a2, 1000); b.printB(); } class ABC { public: ABC(int a, int b, int c, int m) :m_m(m) { cout << "ABC(int ,int int)" << endl; m_a = a; m_b = b; m_c = c; } ~ABC() { cout << "~ACB()" << endl; } private: int m_a; int m_b; int m_c; const int m_m;//常量 }; class ABCD { public: ABCD(int a, int b, int c, int d, int m) : m_abc(a, b, c, m), m_d(d) { } ABCD(ABC&abc, int d) :m_abc(abc) { m_d = d; } private: int m_d; ABC m_abc; }; int main(void) { B b(10, 20, 300); b.printB(); ABC abc(10, 20, 30, 666); ABCD abcd(1, 2, 3, 4, 666); ABCD abcd1(abc, 40); return 0; }

    **当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通 过成员初始化列表进行初始化,**因为这两种对象要在声明后马上初始化,而在 构造函数中,做的是对他们的赋值,这样是不被允许的。 

    初始化列表中的初始化顺序,与声明顺序有关,与前后赋值顺序无关。

    构造中调用构造是危险的行为,是不被允许的

    Processed: 0.013, SQL: 9