c++构造函数、析构函数、和拷贝构造函数

#include <iostream>

using namespace std;

//一般类创建出来的对象是保存在栈空间的
class Test
{
public:
    //当类里没有显式的定义构造函数和析构函数时,类里会帮你默认创建一个空构造函数和空析构函数
    //当类里有显式的定义构造函数和析构函数时,类里就不会帮你创建构造函数和析构函数
    //函数名和类名一样,且没有返回值,即为构造函数,构造函数与对象创建同时进行
    //构造函数的存在意思是为了当初始化这个类的一个对象时,此时对象里的私有变量是没有赋值的,当你再给私有变量赋值时,它与初始化对象是有时间差的,当多线程的时候,可能会造成错误
    /*
    Test t1;
    ...(时间差)
    t1.init(10, 20);
    */
    Test(int x, int y)
    {
        my_x = x;
        my_y = y;
    }
    //构造函数也可以重载
    Test(int x)
    {
        my_x = x;
        my_y = 10;
    }
    //无参构造函数
    Test()
    {
        my_x = 1;
        my_y = 1;
        //这时候的内存分布情况为:这个类的对象的私有变量my_x、my_y、char *name,而这个对象又在堆中开辟了一块64字节的空间,并且name指向它,但这块堆空间并不在这个对象空间内部
        name = (char *)malloc(64);
    }

    //析构函数和构造函数一样,没有返回值,但析构函数也没有参数,且不能重载
    //在一个对象被回收之前,会自己触发析构函数
    //析构函数存在的意义就是对象空间被释放前一定会触发析构函数,这样析构函数就可以用来释放对象开辟出来的不属于对象内部的空间,防止内存泄漏(存在没有被释放的内存空间即称为内存泄漏)
    ~Test()
    {
        cout << "~Test()..." << endl;
        if (name != NULL)
        {
            free(name);
            cout << "free success" << endl;
        }
    }
private:
    int my_x;
    int my_y;
    char *name;
};

class Animal
{
public:
    //显式的构造函数
    Animal(int x, int y)
    {
        my_x = x;
        my_y = y;
    }
    //显式的拷贝构造函数,函数名仍为类名,当函数参数一定要为const修饰的同类型对象的引用
    Animal(const Animal &another)
    {
        my_x = another.my_x;//对象another在相同类里,可以直接调用私有变量,且满足类内部可以调用私有变量的规则
        my_y = another.my_y;
        cout << "Test(const Test &)" << endl;
    }
    //如果不显式的提供拷贝构造函数,系统会默认帮你创建一个拷贝构造函数,默认拷贝函数形式如下:
    //默认拷贝构造函数形参为同类型的对象,且会把同类型对象的私有变量的值都赋给新建对象的私有变量
    /*
    Animal(const Animal &another)
    {
        my_x = another.my_x;
        my_y = another.my_y;
    }
    */

private:
    int my_x;
    int my_y;
};

class Dog
{
public:
    //1.当类中不包含显式构造函数和显式拷贝构造函数时,编译器会为我们自动提供默认构造函数和默认拷贝构造函数
    //2.当类中包含显式的构造函数不包含显式的拷贝构造函数时,编译器会为我们自动提供默认拷贝构造函数
    //3.当类中包含显式的拷贝构造函数不包含显式的构造函数时,编译器不会为我们提供默认构造函数,所以直接定义对象Dog dog;这种写法是错误的
    Dog(const Dog &another)
    {
        my_x = another.my_x;
        my_y = another.my_y;
    }
private:
    int my_x;
    int my_y;
};

class Ball
{
public:
    //显式无参构造函数
    Ball()
    {
        
    }
    //显式有参构造函数
    Ball(int x, int y)
    {
        my_x = x;
        my_y = y;
    }
    //显式拷贝构造函数
    Ball(const Ball &another)
    {
        my_x = another.my_x;
        my_y = another.my_y;
    }
    //显式析构函数
    ~Ball()
    {
        cout << "Ball" << endl;
    }
    void printBall()
    {
        cout << "my_x=" << my_x << endl;
        cout << "my_y=" << my_y << endl;
    }
private:
    int my_x;
    int my_y;
};

void creatBall1()
{
    //下面这两段代码最后执行析构函数的顺序是先b2再b1,谁最先调用构造函数,谁就最后调用析构函数,防止发生冲突
    //调用构造函数
    Ball b1(10, 20);
    //调用拷贝构造函数
    Ball b2(b1);//等价于Ball b2 = b1;
}

//下面两个函数的运行顺序:
//1.先调用b1的构造函数创建对象b1
//2.然后调用拷贝构造函数创建新对象b,func(Ball b);等价于Ball b(b1);/Ball b = b1;
//3.然后调用对象b的析构函数
//4.然后调用对象b1的析构函数
//这里的注意点是在函数func中是新建了对象b,不是仍用对象b1
void func1(Ball b)
{
    b.printBall();
}
//当函数参数为引用时,b与b1为同一对象,那么则不创建新的对象,所以只调用一次构造函数和一次析构函数
void func2(Ball &b)
{
    b.printBall();
}
void creatBall2()
{
    Ball b1(10, 20);
    func1(b1);
    func2(b1);
}

int main()
{
    //这种写法是既定义了对象t1,又调用了构造函数进行初始化,这样变量构建完以后就已经被赋值了,不会出现私有变量为乱码现象
    //也可以这样理解,构造函数即为构建对象的函数,调用这个函数闯将对象需要给对象一个名称,并要符合构造函数形参
    //Test t1(10, 20);这是调用构造函数,Test t1(t)/Test t1 = t;这两个是调用拷贝构造函数
    Test t1(10, 20);
    Test t2(2);
    //下面一段代码即为调用无参构造函数,等价于Test t3();
    Test t3;

    //如果显式的定义了构造函数,那么初始化对象时,要按照构造函数的格式进行有参初始化,不能直接只定义对象名,例:Animal a1;这种就会报错,因为不存在无参构造函数
    Animal a1(10, 20);
    //下面这种形式初始化对象a2即为调用了类里的拷贝构造函数,把对象a1里的私有变量全部赋值给对象a2里的私有变量
    //下面两段代码都是调用拷贝构造函数,意思是完全一样的,注意不论是构造函数还是拷贝构造函数,都只有在初始化的时候才会调用,其后对对象的操作都不再属于构造函数和拷贝构造函数了
    Animal a2(a1);//完全等驾驭Animal a2 = a1;都是调用拷贝构造函数
    Animal a3 = a1;
    //下面两段代码并没有调用拷贝构造函数,第一段代码是调用了构造函数进行初始化,第二段代码是进行赋值,与拷贝构造函数一点关系都没有,拷贝构造函数和构造函数一样,都只在初始化时调用
    Animal a4(1, 1);
    a4 = a1;

    //Dog dog;

    creatBall1();
    creatBall2();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/tulipless/article/details/80449419