文号
t71049
阅读
4536
回复
4
收藏
1
C++定式模型 - 解决Big Three问题的方法
2015/04/25
IP:四川
对于C++初学者来说最头疼的,肯定就是Big Three了,因为这其实是C++比较蛋疼的一个地方,提升了灵活性但增大了耦合性,不过还是有办法解决它的。

所谓Big Three是指『复杂C++类』(指管理有外部资源的基类,如CFile,CArray,CString等)必须具有如下的功能:
1.赋值运算符
2.复制构造函数
3.析构函数

但是,实际上它们通常具有如下关系:
赋值运算符 = 析构函数 + 复制构造函数

为了降低耦合性,需要重新组合它们的功能,解决办法是:

一、建立两个模拟函数:CopyFrom和Dispose
·在Dispose中,丢弃所有资源
·在CopyFrom中,复制内存(memcpy)及所有资源

二、在默认构造函数中,事先清空数据(memset)为0,并进行必要的初始化,清除垃圾数据

三、建立C++定式模型
·在复制构造函数中,调用CopyFrom
·在析构函数中,调用Dispose
·在operator=中,调用Dispose和CopyFrom

这个模型适用于『复杂的C++类』,仅包括自释放成员的简单C++类不需要这个模型。
这种方法实际上有效地实现了类似其它高级语言的复制机制。
模式并非一成不变,如果你不需要某种功能,完全可以砍掉,更可以进一步地修改以符合自己的需求。

class MyClass
{
private:
    // 两个模拟函数
    void CopyFrom(MyClass& src);
    void Dispose();
     
public:
    // 需要自己写的函数
    MyClass(); // 默认构造函数(清空内存并设置默认值)
    MyClass(int a, int b, char *str); // 常规构造函数
     
public:
    // C++定式模型:丢弃旧的,拷贝新的
    ~MyClass() { Dispose(); } // 析构函数(丢弃旧的)
    MyClass(MyClass& src) { CopyFrom(src); } // 复制构造函数(拷贝新的)
    MyClass& operator=(MyClass& src) { // 赋值运算符(丢弃旧的,拷贝新的)
        Dispose();
        CopyFrom(src);
        return *this;
    }
     
public:
    int a;
    int b;
    char *str;
};
     
// 【CopyFrom函数】作用:复制内存和外部资源
void MyClass::CopyFrom(MyClass& src)
{
    // 复制内存
    memcpy(this, &src, sizeof(MyClass));
     
    // 复制外部资源
    // 先判断是否来了新资源,如果来了则复制
    if (src.str != NULL) {
        this->str = new char[strlen(src.str)+1];
        strcpy(this->str, src.str);
    }
}
     
// 【Dispose函数】作用:销毁旧资源
void MyClass::Dispose()
{
    if (this->str != NULL)   // 判断是否存在旧资源
    {
        delete[] this->str; // 删除旧资源
    }
}
     
// 【默认构造函数】
// 任务:清空内存,设置默认值
MyClass::MyClass() {
    memset(this, 0, sizeof(MyClass));
    // TODO: 设置默认值
}
     
     
// 【常规构造函数】
// 任务:按照常规方法初始化数据
MyClass::MyClass(int a, int b, char *str)
{
    this->a = a;
    this->b = b;
     
    if (str != NULL) {
        this->str = new char[strlen(str)+1];
        strcpy(this->str, str);
    } else {
        // 注意:常规构造函数需要初始化所有成员,防止产生垃圾数据
        this->str = NULL;
    }
}
[ 修改于11年8个月前 - 2015/04/28 01:21:05 ]
acmilan作者
11年8个月前修改于 11年8个月前IP:四川
764862
1楼
当然,如果你想做一个“不动性赋值”特性的C++类(即赋值运算符只传递状态而不传递对象本身,类似VB6风格)
——这不属于通常意义上的赋值运算符的功能。。。就别用这个模型了,乖乖地写Big Three吧
引用
评论
acmilan作者
11年8个月前IP:四川
764876
2楼
当然如果懒得写的话,也可以直接把复制构造函数和赋值运算符都设为private(不用编写代码)
对于文件、网络连接等不可复制资源,可以专门写一个全局类统一管理它们的生命周期,而不是由各个类自行管理
引用
评论
acmilan作者
11年7个月前IP:四川
769106
3楼
修正一处,多谢@金星凌日 提醒
class MyClass
{
private:
    // 两个模拟函数
    void CopyFrom(MyClass& src);
    void Dispose();
        
public:
    // 需要自己写的函数
    MyClass(); // 默认构造函数(清空内存并设置默认值)
    MyClass(int a, int b, char *str); // 常规构造函数
        
public:
    // C++定式模型:丢弃旧的,拷贝新的
    ~MyClass() { Dispose(); } // 析构函数(丢弃旧的)
    MyClass(MyClass& src) { CopyFrom(src); } // 复制构造函数(拷贝新的)
    MyClass& operator=(MyClass& src) { // 赋值运算符(丢弃旧的,拷贝新的)
        if (&src != this) { // 【修正】:判断自赋值情况
            Dispose();
            CopyFrom(src);
        }
        return *this;
    }
        
public:
    int a;
    int b;
    char *str;
};
引用
评论
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的