C/C++:最佳实践
目录
本篇记录一些C/Cpp开发中的一些最佳实践。
写法推荐
- 在内置类型(int、float),以及一些小型非基本类型(pair、complex)的传递中,使用
const auto
,而非const auto &
。这是出于简化生命周期、编译器优化潜力、防止引用悬挂等问题的考虑。
资源获取和五法则
五法则是指:拷贝构造、拷贝赋值运算符、析构函数、移动构造、移动赋值运算符。其中后两者是在C++11之后提出的。在此之前,只有三法则。
从C++11开始,C++为一系列情况,提供了移动语义:无法拷贝、不必要的拷贝。
class MyClass {
public:
MyClass(MyClass&&) noexcept {/* ... */}
MyClass& operator=(MyClass&&) noexcept {/* ... */}
}
移动语义只应该在有必要的时候才进行使用。如果类型中,不涉及到资源管理,那么移动就不一定有价值。比如类型中都是内置类型(int、float),对他们的移动和拷贝没有性能上的区别。
另外如果内部成员有混合的情况,比如一些内置类型,和一些资源类型。这时的移动需要格外注意。编译器生成的默认的移动语义的函数,在对待内置类型是拷贝,而资源类型则会进行移动。此时可能造成后续下标越界。例如
struct Item;
struct Container {
vector<Item> items;
int chooseItem;
Item getChoose(){
return items[chooseItem];
}
}
Container a;
// 一系列赋值
// a.push_back(Item());
// a.push_back(Item());
// a.push_back(Item());
a.chooseItem = 2;
auto b = std::move(a);
// 强烈不推荐,对move后的内容进行操作本身就不安全
// 这里演示只是为了说明,默认的移动赋值运算符,在对待内置类型是直接拷贝
auto i = a.getChoose(); // 异常(chooseItem已经超过items范围)
noexcept
在移动构造、移动赋值运算符、析构函数中,鼓励添加noexcept,以提示编译器进行优化。当然如果你的程序里确实需要抛出异常,那就还是抛出来。
STL
- 智能指针:
- weak_ptr:A、B类内各用weak_ptr保存对方,在其他地方使用A、B时可以随意使用shared_ptr、unique_ptr
实用工具
- 一款能够查看C/C++编译的汇编代码和原始代码对比情况的工具:godbolt.org,也有github仓库可以自行部署