C++面向对象


前言

本文介绍了C++面向对象的细节原理,基于施磊老师的C++基础课程


一、掌握对象的深拷贝和浅拷贝

对象默认的拷贝构造是做内存的数据拷贝,即浅拷贝。关键是对象如果占用外部资源,那么浅拷贝就会出现问题。如:

#include <iostream>

using namespace std;

class SeqStack {
public:
    SeqStack(int size = 10) {
        cout << this << " SeqStack() " << endl;
        _pstack = new int[size];
        _top = -1;
        _size = size;
    }

    ~SeqStack() {
        cout << this << " ~SeqStack() " << endl;
        delete[] _pstack;
        _pstack = nullptr;
    }
private:
    int *_pstack;
    int _top;
    int _size;
};

int main()
{
    /*
        下面的代码运行后会爆core dump,因为在调用析构函数时,首先
        调用s2的析构函数,释放了_pstack指向的资源,之后调用s1的析构
        函数,由于二者指向的是同一块内存区域,因此重复释放导致错误
    */
    SeqStack s;
    SeqStack s1(10);
    SeqStack s2 = s1;

    return 0;
}

1、拷贝构造函数和赋值运算符重载

为了解决这个问题,我们需要自行实现拷贝构造函数,即深拷贝逻辑:

#include <iostream>

using namespace std;

class SeqStack {
public:
    SeqStack(int size = 10) {
        cout << this << " SeqStack() " << endl;
        _pstack = new int[size];
        _top = -1;
        _size = size;
    }

    SeqStack(const SeqStack& src) {
        /*
        为当前对象开辟内存空间,并将原对象中的内容拷贝到当前对象中
        */
        _pstack = new int[src._size];
        for (int i = 0; i <= src._top; i++) {
            _pstack[i] = src._pstack[i];
        }
        _top = src._top;
        _size = src._size;
    }

    ~SeqStack() {
        cout << this << " ~SeqStack() " << endl;
        delete[] _pstack;
        _pstack = nullptr;
    }
private:
    int *_pstack;
    int _top;
    int _size;
};

int main()
{
    SeqStack s;
    SeqStack s1(10);
    SeqStack s2 = s1;

    return 0;
}

上述代码解决了浅拷贝造成的问题,但是,对于赋值操作,依然存在问题,如:

int main()
{
    SeqStack s;
    SeqStack s1(10);
    SeqStack s2 = s1;

    /*
    此处默认会做浅拷贝,造成s2的指针指向s1的内存空间,那么此时s2原来
    的内存空间便没有指针管理,造成问题
    */
    s2 = s1;

    return 0;
}

于是我们还需实现其赋值运算符重载:

void operator= (const SeqStack& src) {
    /*
    自己对自己赋值则直接返回
    */
    if (this == src) {
        return;
    }
    delete[] _pstack;
    _pstack = new int[src._size];
    for (int i = 0; i <= src._top; i++) {
        _pstack[i] = src._pstack[i];
    }
    _top = src._top;
    _size = src._size;
}

2、剑指offer中的实现

待补充

二、掌握构造函数的初始化列表

用于在构造函数的形参列表之后进行类对象的参数初始化,主要有两点要注意:
1、当一个类如B中还有一个成员变量是类A,这时如果A没有提供默认构造函数,就需要初始化列表进行直接构造。如下面的代码会报错,因为A没有默认构造函数:

#include <iostream>

using namespace std;

class A {
public:
    A(int x1, int y1) {
        x = x1;
        y = y1;
    }
private:
    int x, y;
};

class B {
public:
    B(int x1, int y1, int z1) {
        /*
        A没有默认构造函数,不能这么赋值,B对象在构造A对象时会报错
        */
        a = A(x1, y1);
        z = z1;
    }
private:
    A a;
    int z;
};

int main()
{
    return 0;
}

使用初始化列表即可:

class B {
public:
    B(int z1, int x1, int y1) : a(x1, y1), z(z1) {}
private:
    A a;
    int z;
};

2、成员变量的初始化和他们的定义顺序有关,和其在构造函数初始化列表中出现的顺序无关

三、掌握类的各种成员方法以及区别

1、普通成员方法(常对象无法调用)
编译器会添加一个this形参变量
a. 属于类的作用域
b. 调用该方法时,需要依赖一个对象
c. 可以任意访问对象的私有成员

2、static静态成员方法
不会生成this形参
a. 属于类的作用域
b. 用类名作用域来调用方法
c. 只可以访问static静态成员变量

3、const常成员方法
1、属于类的作用域
2、调用依赖一个对象,普通对象和常对象都可以
3、只能读成员变量,不能写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值