C++沉思录——第4章类设计者的核查表

  1. 你的类需要一个构造函数吗?

    • 不需要的话,就不用看了~
  2. 你的数据成员是私有的吗?

    • 找到自己的风格,并一致的使用,并用文档清楚的保存。
template<class T> class Vector{
public:
    int GetLength() const { return length_; } // 获取
    void SetLength(const int& length) { this->length_ = length;} // 设置
private:
    int length_; // 私有
};
  1. 你的类需要一个无参的构造函数吗?

    • 如果一个类已经有了构造函数,但是你想不必显示地初始化它们,则必须显示地写一个无参的构造函 数。
    • 如果一个类没有无参构造函数,那么无法生成该类对象的数组。
  2. 是不是每个构造函数都需要初始化所有的数据成员?
    是也不是!

    • 构造函数的用途就是用一种明确定义的状态来设置对象。对象的状态由数据成员进行反映。因此,每个构造函数都负责为所有的数据成员设置经过明确定义的值。否则,很可能会导致错误!
    • 当然,有时,类会有一些数据成员,它们只在它们的对象存在了一定时间之后才有意义!
  3. 类需要析构函数吗?

    • 主要看该类是否分配了资源,而这些资源又不会由成员函数自动释放,这就足够了。
  4. 类需要一个虚析构函数吗?

    • 有些类需要虚析构函数只是为了声明它的析构函数是虚的。
    • 绝不会成为基类的类不需要虚析构函数(只要继承就需要使用虚析构函数),任何虚析构函数只在继承的情况下才有用。
    • 虚析构函数通常是空的。
  5. 需要复制构造函数吗?

    • 关键在于复制该类对象是否相当于复制其数据成员和基类对象?不相当的话,就需要复制构造函数!
    • 如果在类的构造函数中分配了资源,则可能需要一个显示地复制构造函数来管理资源。
    • 有析构函数(除了空地虚析构函数)的类通常是用析构函数来释放构造函数分配的资源,这通常也说明需要一个复制构造函数。
  6. 你的类需要一个赋值操作符吗?

    • 一个类需要复制构造函数,同理多半也会需要一个赋值操作符。
    • 如果不想用户能够设置类中的对象,就将赋值操作符私有化。
class Thing {
private:
    Thing& operator=(const Thing& my_thing) {
        // ...
        return *this;
    }
};
  1. 赋值操作符可以正确地将对象赋给对象本身吗?
    • 自我赋值经常被错误地应用。总是奉行“先释放旧值,再复制”的话,那么就可能在还没有实施复制之前就把原对象销毁了。

class String {
public:
    String& operator=(const String& s);
    // ...
private:
    char* data;
};
// 很显然但不正确的实现
String& String::operator=(const String& s) {
    delete [] data; // 由于s 和 *this 都指向同一个对象,这里 delete 之后,s 和 *this 指向的那个对象的 data 成员就被释放了
    data = new char[strlen(s.data) + 1]; // 这里就会出错了,s.data 已经被释放了
    strcpy(data, s.data);
    return *this;
}
// 改正 1:直接判断是不是自己复制自己
String& String::operator=(const String& s) {
    if (&s != this) { // 判断 s 对象的地址 和 this(本对象地址)是否一致
        delete [] data;
        data = new char[strlen(s.data) + 1];
        strcpy(data, s.data);
    }
    return *this;
}
// 改正 1:将旧值直接保存,直到将源值复制完成,感觉不如第一种简单
String& String::operator=(const String& s) {
    char* new_data = new char[strlen(s.data) + 1];
    strcpy(new_data, s.data);
    delete data;
    data = new_data;
    return *this;
}
  1. 需要定义关系操作符吗?

    • 如果你的类支持相等操作,那么提供 operator== 和 operator!= 就很有好处。
    • 类似的,如果你的类支持排序关系,那么也最好提供相应的关系操作符。
  2. 删除数组时记得用delete[]
    原因:为了保持 C++ 和 C 兼容性的同时也保证效率。C 程序员们希望在它们写函数时使用 malloc 来分配内存,然后返回给 C++ 函数。之后,它们希望 C++ 函数可以使用 delete 来释放内存。C++ 系统不想占用现有的 C系统的 malloc 函数,因此必须利用原来的 malloc 直接实现 new,而不另起炉灶。因此,C++ 库在释放数组时不一定清楚数组的大小。即使 malloc 把长度值存储在某个位置上,C++库也没有办法保证可移植性的前提下找到这个值。因此,使用一种折中方案,C++要求用户告知要删除的是不是数组。如果是,该实现就可能提供另一个地方来存储长度,因为与数组所需要的内存相比,这个常数的开销会小很多。

    • 好习惯:在删除任何类型的数组时使用 delete []格式仍然是一种很好的习惯~
  3. 记得在复制构造函数和赋值操作符的参数类型前加const!

    • 复制和赋值操作不会改变原对象。
  4. 函数的引用参数必须加const吗?

    • 只有当函数想改变参数时,它才应该不用const声明的引用参数。
  5. 适当的声明成员函数为const

    • 如果一个函数确定不用修改对象,就应该声明为const,这样就只能把它用于 const对象了~
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值