最近在看《Effective C++》这本书,这博客相当于是个浓缩版的总结吧。
在这里你可以大致游览下在 C++ 开发中前人给了我们哪些建议,有机会我觉得最好还是可以看看原书,因为里面会有不少具体的例子告诉你为什么这么做以及这么做的好处。
我们可以视 C++ 为一个由相关语言组成的联邦而非单一语言,例如:
const
,enum
,inline
替换 #define
const
class TextBlock {
public:
const char& operator[] (std::size_t position) const {
// do something
return text[position];
}
char& operator[] (std::size_t position) {
// 调用 const 版本的重载函数,避免代码重复
return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
}
private:
std::string text;
};
其中构造函数最好使用使用成员初值列,而不是在构造函数中使用赋值操作。如:
Person::Person(const std::string& name, const int age)
:mName(name), mAge(age) // 成员初始列
{
// mName = name; // 赋值操作
// mAge = age; // 赋值操作
}
编译器会自动给类创建 default 构造函数、copy 构造函数、赋值操作符(operator=)、析构函数。
class Empty {}; // 等同于下面写法
class Empty {
public:
Empty() {} // default构造函数
Empty(const Empty& rhs) {} // copy构造函数
~Empty() {} // 析构函数
Empty& operator=(const Empty& rhs) {} // copy assignment 操作符
};
将相应的成员函数声明为 private
,并不予实现即可。
如果一个类带有任何 virtual 函数,它都应该拥有一个 virtual 析构函数。
析构函数不要抛出异常。如果析构函数中调用的函数可能抛出异常,那么也要捕捉并吞掉这个异常或结束程序。
在父类构建的过程,virtual 函数还没有下降到子类中去。
*this
的引用operator=
,operator+=
,operator-=
等等赋值运算符,都返回一个 reference to *this。如:
Widget& operator=(const Widget& rhs) {
// do something
return *this;
}
operator=
中处理 “自我赋值”Widget& operator=(const Widget& rhs) {
if (this == &rhs) {
return *this; // 如果是自我赋值,就不做任何事
}
// do something
return *this;
}
auto_ptr
和 shared_ptr
)来管理资源类,避免你忘记 delete 资源类。例如提供一个 get()
方法获取原始资源。
new
和 delete
时要采取相同形式避免发生异常时,导致难以察觉的资源泄漏产生。如:
std::tr1::shared_ptr<Widget> pWidget(new Widget);
processWidget(pWidget);
// 不建议下面这样做,如果 processWidget 发生异常可能造成泄漏
// processWidget(std::tr1::shared_ptr (new Widget));
size
的成员函数返回目前容器的大小。当一个 “必须返回新对象” 的函数,我们就直接返回一个新对象,而不要返回引用或者指针。
private
实现数据的封装性。且可以更方便的控制数据的访问。
如果一个有理数的类 Rational,我们常常喜欢直接使用 int 类型的数和 Rational 对象进行混合运算。那么使用 non-member 函数将是更好的选择,它允许每一个参数都进行隐式类型转换。
class Rational {
public:
Rational(int numerator = 0, int denominator = 1)
: mNumerator(numerator), mDenominator(denominator) {}
int numerator() const { return mNumerator; }
int denominator() const { return mDenominator; }
private:
int mNumerator; // 分子
int mDenominator; // 分母
};
const Rational operator*(const Rational& lhs, const Rational& rhs) {
return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator());
}
int main() {
Rational oneFourth(1, 4);
Rational result;
result = oneFourth * 2;
result = 2 * oneFourth; // 如果不是 non-member 的形式将不支持这种写法
}
当系统的 std::swap
对你的类型效率不高时,提供一个 swap 成员函数,并确保这函数不会抛出异常。
如果提供一个 member swap,也该提供一个 non-member swap 来调用前者。例如:
class WidgetImpl {
public:
// ...
private:
int a, b, c; // 可能有很多数据
std::vector<double> v; // 意味着复制时间很长
// ...
};
// 这个 class 使用 pimpl 手法
// pimpl 是 pointer to implementation 的缩写
class Widget {
public:
Widget(const Widget& rhs) {}
Widget& operator=(const Widget& rhs) {
// ...
*pImpl = *(rhs.pImpl);
// ...
}
// 我们需要交换两个 Widget 对象,但是直接使用 std::swap 将导致很多多余的复制
// 好的做法是只交换两个 WidgetImpl* 指针对象即可,所以我们考虑写一个 swap 方法
void swap(Widget& other) {
using std::swap; // 这个声明至关重要,使得C++编译器能够找到正确的函数调用
swap(pImpl, other.pImpl); // 我们只需要交换 pImpl 即可
}
private:
WidgetImpl* pImpl;
};
// 提供一个 non-member swap 来调用 member swap
namespace std {
template<>
void swap<Widget>(Widget& a, Widget& b) {
a.swap(b);
}
}
将一些变量定义式放在常规的参数检查后面,避免无用的构造方法带来的耗费。有助于增加程序清晰度和改善程序效率。
dynamic_cast
。operator[]
操作允许你获取个别元素的引用。inline
里里外外https://blog.csdn.net/afei__/article/details/83624720