特别备注
本系列文章总结自MJ老师在腾讯课堂30小时快速精通C++和外挂实战-大神MJ精选,相关图片素材均取自课程中的课件。
const成员
image-20210410135026385
◼ const成员:被const修饰的成员变量、非静态成员函数
◼ const成员变量
必须初始化(类内部初始化),可以在声明的时候直接初始化赋值
非static的const成员变量还可以在初始化列表中初始化
class Car {
public:
const int m_price;
Car() :m_price(0) {}
void run() const {
cout << "run() const" << endl;
}
};
◼ const成员函数(非静态)
const关键字写在参数列表后面,函数的声明和实现都必须带const
内部不能修改非static成员变量
image-20210410135547141
内部只能调用const成员函数、static成员函数
非const成员函数可以调用const成员函数
const成员函数和非const成员函数构成重载
非const对象(指针)优先调用非const成员函数
const对象(指针)只能调用const成员函数、static成员函数
class Car {
public:
int m_price;
void run() const {
cout << "run() const" << endl;
}
void run() {
cout << "run()" << endl;
}
};
int main() {
Car car;
car.run();
const Car car2{};
car2.run();
return 0;
}
Run->
run() run() const
引用类型成员
image-20210410140151421
拷贝构造函数(Copy Constructor)
image-20210410140209729
class Car {
public:
int m_price;
int m_length;
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) {
cout << "Car(const Car &car)" << endl;
}
void display() {
cout << "price=" << m_price << ", length=" << m_length << endl;
}
};
- 当利用已存在的对象创建一个新对象时(类似于拷贝),就会调用新对象的拷贝构造函数进行初始化
//main 函数
Car car3(100, 5);
// 利用已经存在的car3对象创建了一个car4新对象
// car4初始化时会调用拷贝构造函数
Car car4(car3);
Run->
Car(int price = 0, int length = 0) Car(const Car &car)
- 拷贝构造函数的格式是固定的,接收一个const引用作为参数
// 拷贝构造函数
Car(const Car &car) :m_price(car.m_price) {
cout << "Car(const Car &car)" << endl;
}
image-20210410142758834
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;
}
};
- 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();
// 这里并没有调用拷贝构造函数(仅仅是简单的赋值操作)
car4 = car3;
car4.display();
Run->
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
image-20210410143145293
调用父类的拷贝构造函数
image-20210410141709206
class Person {
public:
int m_age;
Person(int age = 0) :m_age(age) {}
Person(const Person &person) :m_age(person.m_age) {}
};
class Student : public Person {
public:
int m_score;
Student(int age = 0, int score = 0) :Person(age), m_score(score) {}
Student(const Student &student) :Person(student), m_score(student.m_score) {}
};
浅拷贝、深拷贝
image-20210410143536038
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;
}
};
//main 函数
Car *car = new Car(100, "bmw");
car->display();
Run->
price is 100, name is bmw
//main 函数
char name[] = {'b', 'm', 'w', '\0'};
Car *car = new Car(100, name);
car->display();
Run->
price is 100, name is bmw
- 对象内存分布
image-20210410144436015car对象的m_name指向了栈空间的地址。这样的操作非常危险
修改car的构造函数,早对重开空间,来存储m_name
class Car {
int m_price;
char *m_name;
public:
Car(int price = 0, const char *name = nullptr) : m_price(price) {
if (name == nullptr) return;
// 申请新的堆空间
m_name = new char[strlen(name) + 1]{};
// 拷贝字符串数据到新的堆空间
strcpy(m_name, name);
}
~Car() {
if (m_name == nullptr) return;
delete[] m_name;
m_name = nullptr;
}
void display() {
cout << "price is " << m_price << ", name is " << m_name << endl;
}
};
main 函数
char name[] = {'b', 'm', 'w', '\0'};
Car *car = new Car(100, name);
car->display();
Run->
price is 100, name is bmw
- 对象内存分布
image-20210410150110268main函数:
Car car1(100, "bmw"); Car car2 = car1; car2.display();
run->
price is 100, name is bmw 36-main(2233,0x109af6dc0) malloc: *** error for object 0x7f811bc05930: pointer being freed was not allocated 36-main(2233,0x109af6dc0) malloc: *** set a breakpoint in malloc_error_break to debug
What 发生了什么问题?
编译器默认的提供的拷贝是浅拷贝(shallow copy)
默认拷贝构造内存布局
image-20210410150551354当函数调用完成后 car1 ~Car()
bmw
堆空间释放,当car2 ~Car()时候,对地址已经释放, double free ,程序崩溃!
深拷贝
class Car {
int m_price;
char *m_name;
void copy(const char *name = NULL) {
if (name == NULL) return;
// 申请新的堆空间
m_name = new char[strlen(name) + 1] {};
// 拷贝字符串数据到新的堆空间
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;
}
};
- main函数
Car car1(100, "bmw");
Car car2 = car1;
car2.display();
run->
price is 100, name is bmw
- 内存布局
image-20210410151041762image-20210410151236826可以看到car1 和 car2 有各自的对内存地址,两个对象的修改操作都不会影响另外一个对象的数据!
特别备注
本系列文章总结自MJ老师在腾讯课堂30小时快速精通C++和外挂实战-大神MJ精选,相关图片素材均取自课程中的课件。