C++基础复习·三

    xiaoxiao2021-03-25  68

    1.构造函数与析构函数

    问题:构造函数是什么原理呢?在Test t1(10, 20);中,t1是一个类对象,不是一个函数名, 怎么会和函数调用一样直接双括号加实参呢?他的实现原理是什么呢? 同样Test t1 = Test(10, 20);该构造函数又没有返回值,怎么会作为右值呢?

    class Test { public: Test(int x, int y) { m_x = x; m_y = y; } private: int m_x; int m_y; }; main:: Test t1(10, 20); Test t1 = Test(10, 20);/*产生一个匿名类对象,将该匿名类对象转正为t1变量,未调用等号操作符进行赋值操作*/

    2.默认构造函数和默认析构函数

    如果显示定义一个构造函数和析构函数,那么就会覆盖掉默认的构造函数和析构函数 类中会有一个默认的无参构造函数,一个默认的拷贝构造函数,一个默认的等号操作符 1.当没有任何显示的构造函数(显式的无参构造函数,显式的有参构造函数,显式的拷贝构造函数),默认无参构造函数都会被调用 2.当没有**显示拷贝构造函数**,默认的拷贝构造函数就会调用 3.当没有**显示的析构函数**,默认的析构函数就会被调用

    3.拷贝构造函数

    编译器会有一个默认的拷贝构造函数 单纯的将另一个对象的成员变量拷贝给自己

    void operator=(const Test& another) { m_x = another.m_x; m_y = another.m_y; } Test(const Test& another) { m_x = another.m_x; m_y = another.m_y; } main:: //调用有参数的构造函数 Test t2(10, 20); t2.printTest(); //如果不写Test(const Test& another),系统会自动加上这种构造函数,其实默认的拷贝构造函数 //做的就是将另一个对象的成员变量全部拷贝给自己的成员变量 Test t3(t2); t3.printTest(); //两种调用拷贝构造函数的方法是等价的 Test t4 = t2; t4.printTest(); //----------------分隔符------------ //这个是定义t5时调用无参的构造函数 Test t5; //等号操作符重载,这是一个赋值操作 t5 = t1; //注意,拷贝构造函数会引出深拷贝和浅拷贝问题

    4.构造函数的调用顺序(六种场景)(重点)

    void test1() { //构造函数:谁先创建,先调用谁 Test t1(10, 20); Test t2(t1);//Test t2 = t1; //析构函数:谁先创建,后调用谁 //原理就是栈的入栈出栈,对象定义后进行压栈,释放时进行出栈 } 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; }

    输出: test3 begin… Test(int x, int y)… Test(const Test &)… //调用fun时,相当于 Test t = t1,调用t的拷贝构造函数 func begin… x = 10, m_y = 20 //调用t.printT(); func end… //cout << “func end…” << endl; ~Test()… //调用t的析构函数 test3 end… //cout << “test3 end…” << endl; ~Test()… //调用t1的析构函数


    Test func2(){ cout << "func2 begin..." << endl; Test temp(10, 20); temp.printT(); cout << "func2 end..." << endl; return temp;//这一步调用拷贝构造函数,匿名的对象 = temp 匿名对象.拷贝构造(temp) }//↑ 这个临时变量temp到这个位置时才会调用析构函数 void test4(){ cout << "test4 being.. " << endl; func2();/*返回一个匿名对象。 当一个函数返回一个匿名对象的时候,函数外部没有任何变量去接收它, 这个匿名对象将不会再被使用,(找不到), 编译会直接将个这个匿名对象*/ //回收掉,而不是等待整改函数执行完毕再回收. //匿名对象就被回收。 cout << "test4 end" << endl; }

    输出: test4 being.. func2 begin… Test(int x, int y)… x = 10, m_y = 20 func2 end… Test(const Test &)… ~Test()…//先将temp析构 ~Test()…//返回的临时变量因为没有变量承接,func2()调用完毕后编译器就就把这个临时变量析构 test4 end


    Test& func2(){ cout << "func2 begin..." << endl; Test temp(10, 20); temp.printT(); cout << "func2 end..." << endl; return temp; } void test4(){ cout << "test4 being.. " << endl; Test t1 = func2();//返回一个temp的引用 t1.printT(); cout << "test4 end" << endl; }

    输出: test4 being.. func2 begin… Test(int x, int y)… x = 10, m_y = 20 func2 end… ~Test()… //将temp析构掉 Test(const Test &)… //拷贝构造,Test t1 = func2();会返回一个temp的引用 x = -858993460, m_y = -858993460 test4 end ~Test()…


    Test func2(){ cout << "func2 begin..." << endl; Test temp(10, 20); temp.printT(); cout << "func2 end..." << endl; return temp; }//匿名的对象 = temp 匿名对象.拷贝构造(temp) void test5(){ cout << "test 5begin.. " << endl; Test t1 = func2(); //会不会触发t1拷贝构造来 t1.拷贝(匿名)? //并不会触发t1拷贝,而是 将匿名对象转正 t1, //把这个匿名对象 起了名字就叫t1. cout << "test 5 end.." << endl; }

    输出: test 5begin.. func2 begin… Test(int x, int y)… x = 10, m_y = 20 func2 end… Test(const Test &)… ~Test()… test 5 end.. ~Test()…


    Test func2(){ cout << "func2 begin..." << endl; Test temp(10, 20); temp.printT(); return temp; } void test6(){ cout << "test6 begin..." << endl; Test t1; //t1已经被初始化了。 t1 = func2(); t1.printT(); cout << "test6 end.." << endl; }

    输出: test6 begin… Test()… func2 begin… Test(int x, int y)… x = 10, m_y = 20 func2 end… Test(const Test &)… ~Test()… Test(const Test &)… ~Test()… //匿名对象在完成赋值后就会被释放掉 x = 10, m_y = 20 test6 end.. ~Test()…


    5.深拷贝和浅拷贝

    触发时机:调用了类中默认的拷贝构造函数或者默认的等号操作符

    浅拷贝示例

    class A { private: int _ma; int _mb; char *_mch; public: A(int a, int b, char *ch) { cout << "A(int a, int b, char *ch)" << endl; _ma = a; _mb = b; int len = strlen(ch); _mch = (char *)malloc(sizeof(char)*(len + 1)); strcpy(_mch, ch); } //A(A &_newa) //{ //} ~A() { cout << "~A()" << endl; if (_mch != NULL) { free(_mch); _mch = NULL; } } }; void fun1() { A a1(10, 20, "lily"); A a2(a1); } //定义a1时调用有参数的构造函数,在堆区分配了块内存空间,将文字常量区的内容拷贝到该区域 //定义a2时调用默认的拷贝构造函数,默认的拷贝构造函数的主要逻辑就是将a1内存中的所有内容拷贝到a2中 //这种做法虽然对普通形参没有太大的影响,但是对于指针变量而言,隐藏了一个错误 //默认的拷贝构造函数将a1中的内容全部拷贝到a2中后,a2._mch也会指向a1._mch所指向的内存区域 //在fun1函数结束时,a2先调用析构函数,将a2._mch所指向的内存区域释放掉 //接着a1调用析构函数将a1._mch所指向的内存区域再释放一遍,这样对同一块内存区域释放两次,程序会报错 //解决这个问题,可以通过一个深拷贝来完成,在类中定义一个拷贝构造函数,再在堆中创建一个内存区域 A(A &_newa) { _ma = _newa._ma; _mb = _newa._mb; int len = strlen(_newa._mch); _mch = (char *)malloc(sizeof(char)*(len + 1)); strcpy(_mch, _newa._mch); } //这样两个对象的两个指针分别指向两块不同的内存区域

    6.构造函数初始化列表

    1).类对象中包含另一个类的对象 2).如果类对象中包含两个或多个类的对象,在构造函数的初始化列表中的成员对象的初始化顺序 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)//调用默认的拷贝构造函数 { //如果没有构造函数形参列表 //这样定义一个类B中包含有两个类A的对象,在创建B的对象时,初始化成员变量m_a1,m_a2就出现了问题 //1.不能直接m_a1 = a1,这是调用类A的等号操作符进行赋值 //2.也不能直接 m_a1(a1);这样是吧m_a1当成函数来看待,当然这样是错误的 //所以将两个成员变量m_a1,m_a2放在形参列表中初始化 cout << "B(A &a1, A &a2, int b)" << endl; m_b = b; } //构造对象成员的顺序跟初始化列表的顺序无关 //跟成员变量的定义顺序有关 //定义在前的先被初始化 B(int a1, int a2, int b) : m_a1(a1), m_a2(a2) // B(int a1, int a2, int b) : m_a2(a2), m_a1(a1) { 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_a1; A m_a2; }; void test1() { A a1(10), a2(100); B b(a1, a2, 1000); b.printB(); }

    7.new和delete关键字

    new和delete关键字同malloc和free的相同点

    new和malloc一样都是在堆中分配一块内存,然后返回该内存在堆中的地址. delete和free一样,都是将堆中的指定内存释放掉

    用new开辟的空间可以用free来释放掉 用malloc开辟的空间可以用delete来释放掉

    new和delete关键字同malloc和free的不同点

    A).malloc和free是标准库stdlib.h中定义的函数,既然是函数,那在调用时就存在入栈出栈操作.而new和delete关键字是C++的操作符,根C中的sizeof一样,不存在函数调用时的入栈出栈操作 B).用6)中的类A来说 A *temp = (A*)malloc(sizeof(A)); //此时malloc不会将成员变量初始化,如果这时候调用temp.printA()输出的则是乱码 A* temp = new A(10); //new关键字可以在堆中创建一个A空间,同时调用A的构造函数将该空间进行了初始化操作,返回该空间的地址 C).new和delete会触发类的构造函数和析构函数 class Dog { public: Dog(int id,char *name) { cout << "Dog(int id)" << endl; m_id = id; int len = strlen(name); m_name = (char *)malloc(sizeof(char)*(len+1)); strcpy(m_name,name); } ~Dog() { cout << "~Dog()" << endl; if(m_name != NULL) { free(m_name); m_name = NULL; } } void showDog() { cout << "showDog()" << endl; } private: int m_id; char *m_name; }; void fun1() { Dog *d1 = new Dog(10,"SUNNY"); d1->showDog(); if (d1 != NULL) { delete d1; } } /* Dog(int id) showDog() ~Dog() */ //在堆中创建了一个类的对象空间,里面有int,和char*两个成员---->空间A //在构造函数中另外创建了一个m_name空间,用来存储Dog的名字---->空间B //调用 delete d1;时delete负责释放的是“空间A”,它触发了类的析构函数,而析构函数负责释放“空间B” //析构函数不是释放对象本身,而是负责释放对象在堆上额外开辟的内存空间

    8.static成员变量

    初始化方法

    static成员变量必须在类的外部进行初始化 类型 类名::静态成员变量 = 值 class Box { private: int len; int width; public: static int high; Box(int l, int w) { len = l; width = w; } void volume() { cout << "high = " << high << endl; cout << "volume = " << len*width*high << endl; } }; int Box::high = 10;//static成员变量初始化一定要在类外进行

    在public区

    Box b1(10, 20); b1.volume(); Box::high = 20;//static成员变量放在public区,可以在类外进行修改访问 b1.volume();

    在private区

    class Box2 { private: int len; int width; static int high; public: Box2(int l, int w) { len = l; width = w; } void volume() { cout << "high = " << high << endl; cout << "volume = " << len*width*high << endl; } static void setHigh(int h)//如果static成员变量放在private区域,进行修改时必须通过静态成员函数进行修改 { high = h; } }; Box2 b2(10, 20); b2.volume(); Box2::setHigh(20);//因为是静态的,可以通过类名加::进行访问 b2.volume();

    static 成员类外存储,求类大小,并不包含在内

    cout << "sizeof(Goods) = " << sizeof(Goods) << endl; //8个字节,int len; int width; //static int high;在data区,不包含在类内
    转载请注明原文地址: https://ju.6miu.com/read-34931.html

    最新回复(0)