欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++知识点总结(面向对象5-const成员, 拷贝构造函数)

程序员文章站 2022-07-12 15:30:11
...

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++知识点总结(面向对象3-多态)

C++知识点总结(面向对象4-多继承, 静态成员static)
C++知识点总结(面向对象5-const成员, 拷贝构造函数)
C++知识点总结(面向对象6-隐式构造, 友元, 内部类, 局部类)
C++知识点总结(其他语法1-运算符重载)
C++知识点总结(其他语法2-模板, 类型转换, C++11新特性)