011-const成員、拷貝構(gòu)造函數(shù)、淺拷貝和深拷貝

往者不可諫,來者猶可追。
為有犧牲多壯志,敢教日月?lián)Q新天

《C++文章匯總》
上一篇介紹了引用和匯編《010-菱形繼承、虛繼承、多繼承作用和靜態(tài)成員》,本文介紹const成員、拷貝構(gòu)造函數(shù)、淺拷貝和深拷貝。

1.const成員

? const成員:被const修飾的成員變量、非靜態(tài)成員函數(shù)
? const成員變量
必須初始化(類內(nèi)部初始化),可以在聲明的時(shí)候直接初始化賦值
非static的const成員變量還可以在初始化列表中初始化
? const成員函數(shù)(非靜態(tài))
const關(guān)鍵字寫在參數(shù)列表后面,函數(shù)的聲明和實(shí)現(xiàn)都必須帶const
內(nèi)部不能修改非static成員變量
內(nèi)部只能調(diào)用const成員函數(shù)、static成員函數(shù)

class Car{
public:
    const int m_price = 0;
    Car():m_price(0){};
    void run() const{
        cout << "run()" << endl;
//        test();//報(bào)錯(cuò)
    }
    void test(){
        run();//可以調(diào)用
    }
};
int main(){
    Car car;
    getchar();
    return 0;
}

非const成員函數(shù)可以調(diào)用const成員函數(shù)

class Car{
public:
    const int m_price = 0;
    Car():m_price(0){};
    void run() const{
        cout << "run() const" << endl;
    }
    void run(){
        cout << "run()" << endl;
    }
};
int main(){
    Car car;
    getchar();
    return 0;
}

const成員函數(shù)和非const成員函數(shù)構(gòu)成重載

class Car{
public:
    const int m_price = 0;
    Car():m_price(0){};
    void run() const{
        cout << "run() const" << endl;
//        test();//報(bào)錯(cuò)
    }
    
    void run(){
        cout << "run()" << endl;
    }
    void test(){
        run();//可以調(diào)用
    }
};
int main(){
    Car car;
    car.run();
    const Car car2;
    car2.run();
    return 0;
}
//輸出
run()
run() const

非const對(duì)象(指針)優(yōu)先調(diào)用非const成員函數(shù)

class Car{
public:
    const int m_price = 0;
    Car():m_price(0){};
    void run(){
        cout << "run()" << endl;
    }
};
int main(){
    Car car;
    car.run();
    const Car car2;
    car2.run();//報(bào)錯(cuò),不能調(diào)用
    return 0;
}

const對(duì)象(指針)只能調(diào)用const成員函數(shù)、static成員函數(shù)

class Car{
public:
    const int m_price = 0;
    Car():m_price(0){};
    void run() const{
        cout << "run() const" << endl;
    }
};
int main(){
    Car car;
    car.run();
    const Car car2;
    car2.run();
    return 0;
}

const對(duì)象調(diào)用static成員函數(shù)

class Car{
public:
    const int m_price = 0;
    Car():m_price(0){};
    void run() const{
        cout << "run() const" << endl;
    }
    static void accelerate(){
        cout << "accelerate()" << endl;
    }
};
int main(){
    Car car;
    car.run();
    const Car car2;
    car2.run();
    car2.accelerate();
    return 0;
}
//輸出
run() const
run() const
accelerate()

2.引用類型成員

? 引用類型成員變量必須初始化(不考慮static情況)
在聲明的時(shí)候直接初始化
通過初始化列表初始化

class Car{
    int age;
    int &m_price = age;
public:
    Car(int &price):m_price(price){}
};

3.拷貝構(gòu)造函數(shù)

? 拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一種
? 當(dāng)利用已存在的對(duì)象創(chuàng)建一個(gè)新對(duì)象時(shí)(類似于拷貝),就會(huì)調(diào)用新對(duì)象的拷貝構(gòu)造函數(shù)進(jìn)行初始化
? 拷貝構(gòu)造函數(shù)的格式是固定的,接收一個(gè)const引用作為參數(shù)

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;
    }
    //拷貝構(gòu)造函數(shù)
    Car(const Car &car){
        cout << "Car(const Car &car)" << endl;
    }
    void display(){
        cout << "price=" << m_price << "length=" << m_length << endl;
    }
};
int main(){
    
    Car car1;
    Car car2(100);
    Car car3(100,5);
    
    //利用已經(jīng)存在的car3對(duì)象,創(chuàng)建了一個(gè)car4新對(duì)象
    //car4對(duì)象初始化時(shí)會(huì)調(diào)用拷貝構(gòu)造函數(shù)
    Car car4(car3);
    
    getchar();
    return 0;
}
//輸出
Car(int price = 0,int length = 0)
Car(int price = 0,int length = 0)
Car(int price = 0,int length = 0)
Car(const Car &car)

若沒有寫拷貝構(gòu)造函數(shù),相當(dāng)于分別拷貝了car3對(duì)象的兩個(gè)成員變量m_price,m_length

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;
    }
    //拷貝構(gòu)造函數(shù)
//    Car(const Car &car){
//        cout << "Car(const Car &car)" << endl;
//    }
    void display(){
        cout << "price=" << m_price << "length=" << m_length << endl;
    }
};
int main(){
    
    Car car1;
    Car car2(100);
    Car car3(100,5);
    
    //利用已經(jīng)存在的car3對(duì)象,創(chuàng)建了一個(gè)car4新對(duì)象
    //car4對(duì)象初始化時(shí)會(huì)調(diào)用拷貝構(gòu)造函數(shù)
    Car car4(car3);
    //若沒有寫拷貝構(gòu)造函數(shù),相當(dāng)于如下
    /*
     car4.m_price = car3.m_price;
     car4.m_length = car3.m_length;
     */
    car4.display();
    
    getchar();
    return 0;
}
//輸出
Car(int price = 0,int length = 0)
Car(int price = 0,int length = 0)
Car(int price = 0,int length = 0)
price=100length=5

自己實(shí)現(xiàn)的拷貝構(gòu)造函數(shù)并沒有執(zhí)行給成員變量賦值操作,需要自己實(shí)現(xiàn)賦值

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;
    }
    //拷貝構(gòu)造函數(shù)
    Car(const Car &car){
        cout << "Car(const Car &car)" << endl;
//        m_price = car.m_price;
//        m_length = car.m_length;
    }
    void display(){
        cout << "price=" << m_price << "length=" << m_length << endl;
    }
};
int main(){
    
    Car car1;
    Car car2(100);
    Car car3(100,5);
    
    //利用已經(jīng)存在的car3對(duì)象,創(chuàng)建了一個(gè)car4新對(duì)象
    //car4對(duì)象初始化時(shí)會(huì)調(diào)用拷貝構(gòu)造函數(shù)
    Car car4(car3);
    //若沒有寫拷貝構(gòu)造函數(shù),相當(dāng)于如下
    /*
     car4.m_price = car3.m_price;
     car4.m_length = car3.m_length;
     */
    car4.display();
    
    getchar();
    return 0;
}
//輸出
Car(int price = 0,int length = 0)
Car(int price = 0,int length = 0)
Car(int price = 0,int length = 0)
Car(const Car &car)
price=0length=0

實(shí)現(xiàn)賦值

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;
    }
    //拷貝構(gòu)造函數(shù)
    Car(const Car &car){
        cout << "Car(const Car &car)" << endl;
        m_price = car.m_price;
        m_length = car.m_length;
    }
    void display(){
        cout << "price=" << m_price << "length=" << m_length << endl;
    }
};
int main(){
    
    Car car1;
    Car car2(100);
    Car car3(100,5);
    
    //利用已經(jīng)存在的car3對(duì)象,創(chuàng)建了一個(gè)car4新對(duì)象
    //car4對(duì)象初始化時(shí)會(huì)調(diào)用拷貝構(gòu)造函數(shù)
    Car car4(car3);
    //若沒有寫拷貝構(gòu)造函數(shù),相當(dāng)于如下
    /*
     car4.m_price = car3.m_price;
     car4.m_length = car3.m_length;
     */
    car4.display();
    
    getchar();
    return 0;
}
//輸出
Car(int price = 0,int length = 0)
Car(int price = 0,int length = 0)
Car(int price = 0,int length = 0)
Car(const Car &car)
price=100length=5

賦值寫成初始化列表

//拷貝構(gòu)造函數(shù)
Car(const Car &car):m_price(car.m_price),m_length(car.m_length){
    cout << "Car(const Car &car)" << endl;
//        m_price = car.m_price;
//        m_length = car.m_length;
}

若只初始化一個(gè)值,則未初始化的值為默認(rèn)值0(在windows環(huán)境下為??臻g默認(rèn)分配cc)

//拷貝構(gòu)造函數(shù)
Car(const Car &car):m_price(car.m_price){
    cout << "Car(const Car &car)" << endl;
//        m_price = car.m_price;
//        m_length = car.m_length;
}
//輸出
Car(const Car &car)
price=100length=0    

4.調(diào)用父類拷貝構(gòu)造函數(shù)

I.若沒有調(diào)用父類的拷貝構(gòu)造函數(shù),父類中的成員變量無法賦值為默認(rèn)值(Mac中為0,Windows中為cc),但子類拷貝構(gòu)造函數(shù)會(huì)調(diào)用父類的構(gòu)造函數(shù),因?yàn)樽宇惖臉?gòu)造函數(shù)(包括拷貝構(gòu)造函數(shù))會(huì)默認(rèn)調(diào)用父類無參的構(gòu)造函數(shù),此時(shí)會(huì)調(diào)用Person(int age = 0):m_age(age),age賦值為0

class Person{
public:
    int m_age;
    Person(int age = 0):m_age(age){
        cout << "Person(int age = 0)" << age << endl;
    }
    Person(const Person &person):m_age(person.m_age){
        cout << "Person(const Person &person):m_age(person.m_age)"<< person.m_age << endl;
    }
};
class Student:public Person{
public:
    int m_score;
    Student(int age=0,int score=0):Person(age),m_score(score){
        cout << "Student(int age=0,int score=0) age:"<< age << " score:"<< score << endl;
    }
    Student(const Student &student):m_score(student.m_score){
        cout << "Student(const Student &student)"<< m_score << endl;
    }
};
int main(){
    Student stu1(18,100);
    Student stu2(stu1);
    cout << stu2.m_age << endl;
    cout << stu2.m_score << endl;
    getchar();
    return 0;
}
//輸出
Person(int age = 0)18
Student(int age=0,int score=0) age:18 score:100
Person(int age = 0)0//子類調(diào)用父類構(gòu)造函數(shù)
Student(const Student &student)100
0
100

II.要想在子類拷貝構(gòu)造函數(shù)中給子類Student中父類的成員變量m_age賦值,則在子類拷貝構(gòu)造函數(shù)中調(diào)用父類的拷貝構(gòu)造函數(shù),因?yàn)楦割愔羔樋梢灾赶蜃宇悓?duì)象

class Person{
public:
    int m_age;
    Person(int age = 0):m_age(age){
        cout << "Person(int age = 0)" << age << endl;
    }
    Person(const Person &person):m_age(person.m_age){
        cout << "Person(const Person &person):m_age(person.m_age)"<< person.m_age << endl;
    }
};
class Student:public Person{
public:
    int m_score;
    Student(int age=0,int score=0):Person(age),m_score(score){
        cout << "Student(int age=0,int score=0) age:"<< age << " score:"<< score << endl;
    }
    Student(const Student &student):Person(student),m_score(student.m_score){
        cout << "Student(const Student &student)"<< m_score << endl;
    }
};
int main(){
    Student stu1(18,100);
    Student stu2(stu1);
    cout << stu2.m_age << endl;
    cout << stu2.m_score << endl;
    getchar();
    return 0;
}
//輸出
Person(int age = 0)18
Student(int age=0,int score=0) age:18 score:100
Person(const Person &person):m_age(person.m_age)18
Student(const Student &student)100
18
100

III.若自己不實(shí)現(xiàn)拷貝構(gòu)造函數(shù),則默認(rèn)會(huì)將stu1中的所有成員變量的值都拷貝到stu2中

class Person{
public:
    int m_age;
    Person(int age = 0):m_age(age){
        cout << "Person(int age = 0)" << age << endl;
    }
//    Person(const Person &person):m_age(person.m_age){
//        cout << "Person(const Person &person):m_age(person.m_age)"<< person.m_age << endl;
//    }
};
class Student:public Person{
public:
    int m_score;
    Student(int age=0,int score=0):Person(age),m_score(score){
        cout << "Student(int age=0,int score=0) age:"<< age << " score:"<< score << endl;
    }
//    Student(const Student &student):Person(student),m_score(student.m_score){
//        cout << "Student(const Student &student)"<< m_score << endl;
//    }
};
int main(){
    Student stu1(18,100);
    Student stu2(stu1);
    cout << stu2.m_age << endl;
    cout << stu2.m_score << endl;
    getchar();
    return 0;
}
//輸出
Person(int age = 0)18
Student(int age=0,int score=0) age:18 score:100
18
100

5.拷貝構(gòu)造函數(shù)注意點(diǎn)

兩個(gè)對(duì)象初始化后,car4 = car3不會(huì)調(diào)用拷貝構(gòu)造函數(shù),因?yàn)榇藭r(shí)沒有初始化新對(duì)象,car4和car3的內(nèi)存空間已經(jīng)分配完畢,car4=car3僅僅是簡簡單單的拷貝賦值操作

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;
    }
    Car(const Car &car):m_price(car.m_price),m_length(car.m_length){
        cout << "Car(const Car &car)" << endl;
    }
    void display(){
        cout << "price:" << m_price << " length:" << m_length << endl;
    }
};
int main(){
    Car car1(100,5);//Car(int price=0,int length=0)
    Car car2(car1);//Car(const Car &car)
    Car car3 = car2;//Car(const Car &car)
    Car car4;//Car(int price=0,int length=0)
    car4.display();//price:0 length:0
    car4 = car3;//不會(huì)調(diào)用拷貝構(gòu)造函數(shù),因?yàn)榇藭r(shí)不是在初始化新對(duì)象,僅僅是賦值操作
    car4.display();//price:100 length:5
    getchar();
    return 0;
}
//輸出
Car(int price=0,int length=0)
Car(const Car &car)
Car(const Car &car)
Car(int price=0,int length=0)
price:0 length:0
price:100 length:5

6.深拷貝和淺拷貝

?編譯器默認(rèn)的提供的拷貝是淺拷貝(shallow copy)

將一個(gè)對(duì)象中所有成員變量的值拷貝到另一個(gè)對(duì)象
如果某個(gè)成員變量是個(gè)指針,只會(huì)拷貝指針中存儲(chǔ)的地址值,并不會(huì)拷貝指針指向的內(nèi)存空間
可能會(huì)導(dǎo)致堆空間多次free的問題
?如果需要實(shí)現(xiàn)深拷貝(deep copy),就需要自定義拷貝構(gòu)造函數(shù)
將指針類型的成員變量所指向的內(nèi)存空間,拷貝到新的內(nèi)存空間

淺拷貝存在安全隱患

新建的car對(duì)象存在于堆空間,堆空間的指針指向存在于棧空間的name2地址,一旦main函數(shù)結(jié)束,name2所在的??臻g被釋放,則堆空間的成員變量指向了一塊被釋放的??臻g區(qū)域,此塊棧空間隨時(shí)可能被覆蓋,存在數(shù)據(jù)不安全

class Car{
    int m_price;
    char *m_name;
public:
    Car(int price=0,char *name = NULL):m_price(price),m_name(name){}
    void display(){
        cout << "price is " << m_price << ",name is " << m_name << endl;
    }
};
int main(){
//    const char *name = "bmw";
    char name2[] = {'b','m','w','\n'};
    Car *g_car = new Car(100,name2);//傳入name2,是一個(gè)棧空間的地址,堆空間的對(duì)象的成員變量m_name指向棧空間的地址,一旦函數(shù)結(jié)束棧空間被回收,指向一塊不安全區(qū)域
    g_car->display();
    getchar();
    return 0;
}
//輸出
price is 100,name is bmw

在構(gòu)造函數(shù)中開辟堆空間進(jìn)行深拷貝,const參數(shù)能接收const和非const參數(shù),非const參數(shù)只能接受非const參數(shù),不管外面?zhèn)魅氲氖嵌芽臻g還是??臻g的地址,均重新申請(qǐng)堆空間保存不會(huì)存在數(shù)據(jù)安全問題


圖片.png
class Car{
    int m_price;
    char *m_name;
public:
    Car(int price=0,const char *name = NULL):m_price(price){
        if(name == NULL) return;
        //申請(qǐng)新的堆空間
        m_name = new char[strlen(name)+1]{};
        //拷貝字符串?dāng)?shù)據(jù)到新的堆空間
        strcpy(m_name, name);
    }
    ~Car(){
        if (m_name == NULL) return;
        delete[] m_name;
        m_name = NULL;
    }
    void display(){
        cout << "price is " << m_price << ",name is " << m_name << endl;
    }
};
int main(){
//    const char *name = "bmw";
    char name2[] = {'b','m','w','\n'};
    Car *g_car = new Car(100,name2);//傳入name2,是一個(gè)棧空間的地址,堆空間的對(duì)象的成員變量m_name指向??臻g的地址,一旦函數(shù)結(jié)束??臻g被回收,指向一塊不安全區(qū)域
    g_car->display();
    getchar();
    return 0;
}
//輸出
price is 100,name is bmw

自己不實(shí)現(xiàn)拷貝構(gòu)造函數(shù),默認(rèn)進(jìn)行的賦值操作是淺拷貝,car1和car2的成員變量m_name都指向同一片內(nèi)存空間,釋放時(shí)這塊空間會(huì)被釋放兩次,會(huì)崩潰pointer being freed was not allocated,malloc:*** set a breakpoint int malloc_error_break to debug,如何避免?自己實(shí)現(xiàn)深拷貝

class Car{
    int m_price;
    char *m_name;
public:
    Car(int price=0,const char *name = NULL):m_price(price){
        if(name == NULL) return;
        //申請(qǐng)新的堆空間
        m_name = new char[strlen(name)+1]{};
        //拷貝字符串?dāng)?shù)據(jù)到新的堆空間
        strcpy(m_name, name);
    }
    ~Car(){
        if (m_name == NULL) return;
        delete[] m_name;
        m_name = NULL;
    }
    void display(){
        cout << "price is " << m_price << ",name is " << m_name << endl;
    }
};
int main(){
    
    Car car1(100,"bmw");
    Car car2 = car1;
    car2.display();
    getchar();
    return 0;
}
//輸出
price is 100,name is bmw

淺拷貝(shallow copy):指針類型的變量只會(huì)拷貝地址值,深拷貝(deep copy):將指針指向的內(nèi)容拷貝到新的存儲(chǔ)空間

深拷貝:實(shí)現(xiàn)拷貝構(gòu)造函數(shù)重新開辟內(nèi)存空間進(jìn)行深拷貝,若對(duì)象中的成員變量類型均是int類型,則不需要重寫拷貝構(gòu)造函數(shù),默認(rèn)int類型是直接將值拷貝過去,當(dāng)成員變量有指針類型指向存儲(chǔ)空間時(shí)才需要實(shí)現(xiàn)拷貝構(gòu)造函數(shù)開辟空間將值拷貝過去


圖片.png
class Car{
    int m_price;
    char *m_name;
    void copy(const char *name = NULL){
        if(name == NULL) return;
        //申請(qǐng)新的堆空間
        m_name = new char[strlen(name)+1]{};
        //拷貝字符串?dāng)?shù)據(jù)到新的堆空間
        strcpy(m_name, name);
    }
public:
    Car(int price=0,const char *name = NULL):m_price(price){
        copy(name);
    }
    Car(const Car &car):m_price(car.m_price){
        copy(car.m_name);
    }
    ~Car(){
        if (m_name == NULL) return;
        delete[] m_name;
        m_name = NULL;
    }
    void display(){
        cout << "price is " << m_price << ",name is " << m_name << endl;
    }
};
int main(){
    Car car1(100,"bmw");
    Car car2 = car1;
    car2.display();
        getchar();
    return 0;
}
//輸出
price is 100,name is bmw

若成員變量是對(duì)象,則不需要重寫拷貝構(gòu)造函數(shù)進(jìn)行深拷貝,相當(dāng)于Person對(duì)象有兩個(gè)int類型的成員變量

class Car{
    int m_price;
}
class Person{
    int m_age;
    Car car;
};
int main(){
  Person person1;
  Person person2 = person1;
}

若成員變量是對(duì)象的指針,則需要重寫拷貝構(gòu)造函數(shù)進(jìn)行深拷貝,也要根據(jù)需求定,若想兩個(gè)Person對(duì)象擁有同一輛車,則無需進(jìn)行深拷貝

class Car{
    int m_price;
}
class Person{
    int m_age;
    Car *car;
};
int main(){
  Person person1;
  Person person2 = person1;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容