往者不可諫,來者猶可追。
為有犧牲多壯志,敢教日月?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ù)安全問題

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;
}
