C++知识点总结(面向对象5-const成员, 拷贝构造函数)
const成员, 引用成员
const成员: 被const修饰的成员变量, 非静态成员函数.
const成员变量:
1.必须初始化(必须在类内部初始化), 可以在声明的时候直接初始化赋值.
2.非static的const成员变量还可以在初始化列表中初始化.
const成员函数(非静态):(重点)
1.const关键字写在参数列表后面, 函数的声明和实现都必须带const
2.内部不能修改非static成员变量.
static成员变量的内存全世界只有一份, 哪里都可以修改, 而这个const的作用是限制了这个成员函数里面不能修改非static的成员变量.
3.内部只能调用const成员函数, static成员函数.
4.非const成员函数可以调用const成员函数
5.const成员函数和非const成员函数可以构成重载.
非const对象(指针)优先调用非const成员函数
const对象(指针)只能调用const成员函数, static成员函数.
#include <iostream>
using namespace std;
class Car {
public:
const int mc_price;
Car() : mc_price(0) {}
void run() const {
cout << "run()const" << endl;
}
void run() {
cout << "run()" << endl;
}
};
int main()
{
Car car;
car.run();
const Car car1;
car1.run();
return 0;
}
引用类型成员
1.引用类型成员变量必须初始化(不考虑static情况, 很少)
2.在声明的时候直接初始化, 或者通过初始化列表初始化.
#include <iostream>
using namespace std;
class Car {
int m_age;
int &m_price = m_age;
public:
Car(int &price) : m_price(price) {}
};
int main()
{
return 0;
}
拷贝构造函数1(Copy Constructor) (重点)
1.拷贝构造函数是构造函数的一种
2.当利用已存在的对象创建一个新对象时(类似于拷贝), 就会调用新对象的拷贝构造函数进行初始化.
3.拷贝构造函数的格式是固定的, 接收一个const引用作为参数
#include <iostream>
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;
}
// 拷贝构造函数
Car(const Car &car) {
cout << "Car(const Car &car)" << endl;
m_price = car.m_price;
m_lengh = car.m_lengh;
}
void display() {
cout << "price =" << m_price << ", length = " << m_length << endl;
}
};
int main()
{
Car car1;
Car car2(100);
Car car3(100, 5);
// 利用已经存在的car3对象创建了一个car4新对象
// car4初始化时会调用拷贝构造函数
Car car4(car3);
car4.display();
return 0;
}
默认时, 如果没有写拷贝构造函数的话, 就不会帮你生成一个拷贝构造函数, 那他是怎么拷贝的呢?
默认的拷贝操作是: 类似于car4.m_price = car3.m_price; car4.m_length = car3.m_length;
从car3对象的地址开始的8个字节覆盖掉car4的8个字节
默认的拷贝会这个旧对象的所有数据(字节)全部覆盖掉另外一个对象的所有字节.
有写拷贝构造函数时: 会调用拷贝构造函数去初始化对象.此时系统默认的拷贝操作就不存在了.
拷贝构造2-调用父类的拷贝构造函数
#include <iostream>
using namespace std;
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继承Person所以创建Student对象时, 又有age又有score
Student(const Student &student) : Person(student), m_score(student.m_score) {}
// 调用父类的拷贝构造函数
};
int main()
{
Student stu1(18, 100);
Student stu2(stu1);
cout << stu2.m_age << endl;
cout << stu2.m_score << endl;
return 0;
}
拷贝构造3-注意点
Car car1(100, 5);
Car car2(car1); // 拷贝构造
Car car3 = car2; // 拷贝构造与上一个等价(两种写法是等价的)创建car3的同时传了个car2, 通过car2来创建car3, 通过已经存在的对象创建新对象
Car car4;
car4 = car3; // car4是已经存在的对象(不是在创建新对象), 不会调用拷贝构造.仅仅是简单的赋值操作, 构造函数是在对象创建完之后马上调用的.
拷贝构造4-浅拷贝, 深拷贝
编译器默认的提供的拷贝是浅拷贝(shallow copy)
1.将一个对象中所有的成员变量的值拷贝到另一个对象
2.如果某个成员变量是个指针, 只会拷贝指针中存储的地址值, 并不会拷贝指针指向的内存空间
3.可能会导致堆空间多次free的问题(如果堆空间指向栈空间, 栈空间无法控制它的生命周期, 如果栈空间释放了, 堆空间的指针就指向了已经销毁的空间, 就成了野指针.)所以所有的堆空间指向栈空间都是危险的.
如果需要实现深拷贝(deep Copy), 就需要自定义拷贝构造函数
将指针类型的成员变量所指向的内存空间, 拷贝到新的内存空间
#include <iostream>
#include <cstring>
using namespace std;
class Car {
private:
int m_price;
char *m_name;
void copyName(const char *name) {
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) {
copyName(name);
}
Car(const Car &car) : m_price(car.m_price) {
copyName(car.m_name);
}
~Car() {
if (m_name != NULL) {
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();
return 0;
}
总结:拷贝构造函数这个东西只有在你需要进行深拷贝的时候, 才需要写.(类里面全是普通数据类型, 没有指针不需要写拷贝构造函数, 而有指针时, 对象在堆空间时, 堆空间的指针指向栈空间就很危险, 就需要写拷贝构造函数完成深拷贝, 而且如果不写, 直接浅拷贝的话, 会直接将指针里面存储的地址值拷贝过去, 导致两个对象的指针指向同一个东西.)
其他C++系列文章:
C++知识点总结(基础语法1-函数重载, 默认参数)
C++知识点总结(基础语法2-内联函数, const, 引用)
C++知识点总结(面向对象1-类和对象, this指针, 内存布局)
C++知识点总结(面向对象2-构造函数, 初始化列表)C++知识点总结(面向对象4-多继承, 静态成员static)
C++知识点总结(面向对象5-const成员, 拷贝构造函数)
C++知识点总结(面向对象6-隐式构造, 友元, 内部类, 局部类)
C++知识点总结(其他语法1-运算符重载)
C++知识点总结(其他语法2-模板, 类型转换, C++11新特性)
上一篇: 瀑布流的实现方式,移动端的瀑布流,数据多列表渲染方式
下一篇: 整理C知识点--函数(重点)