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、只能读成员变量,不能写

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值