C++入门09 -- 拷贝构造函数,深拷贝,浅拷贝,匿名对象,隐式构造,默认构造函数

拷贝构造函数

  • 拷贝构造函数是构造函数的一种;
  • 当利用一个已经存在的对象创建一个新的对象(类似于拷贝),就会调用新对象的拷贝构造函数进行初始化;
  • 拷贝构造函数的格式是固定的,以const引用作为参数,注意是引用reference,来接收传进来的对象,利用传进来的对象,创建出一个新的对象;
  • 如果不自定义拷贝构造函数,会调用C++系统默认的拷贝构造函数,如果自定义了拷贝构造函数,就调用自定义拷贝构造函数;
  • 针对成员变量是基本数据类型的,使用系统默认的拷贝构造函数就可以了,能将参数对象的所有成员变量的数据全部拷贝到新创建对象对应的成员变量中;
  • 针对成员变量是引用,指针类型的,必须自定义拷贝构造函数,实现深拷贝,而C++系统的数据拷贝默认是浅拷贝,会出现内存问题的,下面将会有详细的案例描述;
#include 

using namespace::std;

class Car {
    int m_price;
    int m_length;
    
public:
    Car(int price = 0,int length = 0) : m_price(price),m_length(length){
        cout << "Car(int price = 0,int length = 0)" << endl;
    }
    
    //1.拷贝构造函数 格式是固定的
    Car(const Car &car){
        this->m_price = car.m_price;
        this->m_length = car.m_length;
        cout << "Car(const Car &car)" <m_name = new char[strlen(name)+1]{};
        //拷贝字符串内容到堆空间
        strcpy(this->m_name, name);
    }
    
    //拷贝构造函数
    Car(const Car &car) : m_price(car.m_price){
        cout << "Car(const Car &car)" << endl;
        //字符串要单独处理
        if (car.m_name == NULL) return;
        //申请堆空间 存储字符串内容
        this->m_name = new char[strlen(car.m_name)+1]{};
        //拷贝字符串内容到堆空间
        strcpy(this->m_name, car.m_name);
    }
    
    ~Car(){
        cout << "~Car()" << endl;
        if (this->m_name == NULL) return;
        delete [] this->m_name;
        this->m_name = NULL;
    }
    
    void display(){
        cout << "price = " << this->m_price << endl;
        cout << "name = " << this->m_name << endl;
    }
};
int main(int argc, const char * argv[]) {
    
    char name[] = {'b','m','w','\0'};
    //堆对象
    Car *car = new Car(100,name);
    
    cout << car->m_price << endl;
    cout << car->m_name << endl;
    
    delete car;
    
    //栈对象
    Car car1(100,"bmw");
    Car car2 = car1;
    
    return 0;
}
  • 针对构造函数中 有字符串成员变量的,如下图所示:
Snip20210814_158.png
  • 针对堆对象的构造函数,需要将name字符串数组的内容从栈区拷贝到堆区,因为栈区的name可能会随时被释放掉,所以构造函数在初始化字符串成员变量时要特殊处理,拷贝字符串内容到新的堆空间中;
  • 针对栈对象的拷贝构造函数,原理图如下所示:
    Snip20210814_160.png
  • 系统默认的拷贝构造函数是浅拷贝,那么拷贝的新对象,都指向同一块内存name,所以需自定义拷贝构造函数,实现name的深拷贝,保证每一个car对象都有自己独立的name;否则会造成name的多次释放以及修改一个car对象,其他car对象也会变动的问题;
  • 析构函数,针对name的堆空间也要做释放处理;

对象类型与返回值

  • 使用对象类型作为函数的参数或者返回值时,会产生一些不必要的中间对象;
#include 

using namespace::std;

class Car {
    int m_price;
    
public:
    Car(int price = 0) : m_price(price){
        cout << "Car(int price = 0)" << this << "-" << m_price << endl;
    }
    
    Car(const Car &car) : m_price(car.m_price){
        cout << "Car(const Car &car)" << this << "-" << m_price << endl;
    }
};

//对象形参
void test(Car car){
    
}

int main(int argc, const char * argv[]) {
    
    Car car1(10);
    //传参的 会产生一个新的对象 因为拷贝构造函数
    test(car1);
  
    return 0;
}
  • test(car1),car1传递给形参car,即Car car = car1会调用car的拷贝构造函数,生成一个新的临时对象,这个临时对象的创建完全没有必要;
  • 在定义函数形参时,为了避免调用拷贝构造函数,生成新的临时对象,定义形参使用对象的引用,就能避免,如下所示:
对象的引用形参
void test(Car &car){
    
}

匿名对象

  • 匿名对象:没有变量名,没有被指针指向的对象,用完之后马上析构销毁;
#include 

using namespace::std;

class Person {

public:
    
    Person(){
        cout << "Person()" << endl;
    }
    
    Person(const Person &person){
        cout << "Person(const Person &person)" << endl;
    }
    
    ~Person(){
        cout << "~Person()" << endl;
    }
    
    void display(){
        cout << "display()" << endl;
    }
};

void test1(Person person){
    
}


int main(int argc, const char * argv[]) {
    
    Person person;

    //匿名对象
    Person().display();

    test1(Person());
    
    return 0;
}

隐式构造

  • C++存在隐式构造的现象,某些情况下,会隐式的调用单参数的构造函数;
#include 

using namespace::std;

class Person {
    int m_age;
public:
    
    Person() : Person(0){
        cout << "Person()" << endl;
    }
    
    Person(int age = 0) : m_age(age){
        cout << "Person(int age = 0)" << endl;
    }
    
    Person(const Person &person){
        cout << "Person(const Person &person)" << endl;
    }
    
    ~Person(){
        cout << "~Person()" << endl;
    }
    
    void display(){
        cout << "display() age = " << this->m_age << endl;
    }
};

void test1(Person person){
    
}

Person test2(){
    return 30;
}
Snip20210814_163.png
  • Person person = 10 等价于 Person person = Person(10)

  • explicit关键字 可禁止使用单参数隐式构造函数;

#include 

using namespace::std;

class Person {
    int m_age;
public:
    
    Person() : Person(0){
        cout << "Person()" << endl;
    }
    
    explicit Person(int age = 0) : m_age(age){
        cout << "Person(int age = 0)" << endl;
    }
    
    Person(const Person &person){
        cout << "Person(const Person &person)" << endl;
    }
    
    ~Person(){
        cout << "~Person()" << endl;
    }
    
    void display(){
        cout << "display() age = " << this->m_age << endl;
    }
};

编译器自动生成的构造函数

  • C++编译器在某些特定的环境下,会默认生成无参的构造函数;
#include 

using namespace::std;

class Person {
    
public:
    int m_age;
    
    Person(int age = 0) : m_age(age){
        cout << "Person() " << endl;
    }
};

int main(int argc, const char * argv[]) {
    
    Person person;//没有生成构造函数 
    person.m_age = 10;
    
    return 0;
}
  • Person person; 没有生成构造函数,实例化对象没有调用函数;
  • 以下环境会生成无参构造函数:
    • 成员变量在声明的同时进行了初始化;
    • 有定义虚函数;
    • 虚继承其他类;
    • 包含了对象类型的成员,且对象成员有构造函数;
    • 父类有构造函数;

你可能感兴趣的:(C++入门09 -- 拷贝构造函数,深拷贝,浅拷贝,匿名对象,隐式构造,默认构造函数)