C++17代码整洁之道
C++代码整洁的基本规范
良好的命名
- 名称应该自解释
- 使用域中的名称
- 避免使用匈牙利命名法(即名称前加类型)
- 不要通过注释禁用代码
函数
-
只做一件事情
-
函数体量要小
-
当你为函数找到一个富有表现力的名称时,名称中没有连词
-
圈复杂度低
-
函数的参数要少
-
-
函数尽可能小
函数应该很小,45行,做多1215行,不能再多了
-
使用容易理解的函数名
std::string head = html.substr(...);
引入一个有意图的名称,将更加容易理解
std::string extractHtmlHeader(const std::string& html) {retrun html.substr(); }
-
函数的参数和返回值
-
函数的参数越少越好
-
函数中最好不要有标志参数
bool
使用
bool
类型说明你很有可能需要在函数中做判断,那为什么不分别用两个函数处理两种情况呢 -
避免使用输出参数,如果要返回多个参数请使用
std::tuple
-
不要传递或者返回
NULL
或nullptr
原因:
- 导致很多空检查,增加代码复杂性
- 对象所有权问题
-
首先在栈上构造对象而不是在堆上
在堆上创建对象给创建者增加了管理资源的责任,而原来所担心的昂贵的拷贝构造成本也不需要担心了,因为
C++11
增加了std::move()
移动语义,即允许资源从一个对象"移动"到另一个对象 -
在函数的参数列表中用**
const
引用代替指针** -
如果一定要使用指针,请使用智能指针
-
尽量使用`const
-
-
用C++类型转换代替C风格类型转换
C风格:
float x{3.12}; int i = int(x);
缺陷:不会在编译期进行类型检查可能产生错误
C++风格
int i = static_cast<int>(x);
优点:在编译期进行类型检查
现代C++的高级概念
资源管理
-
资源申请即初始化
构造时获得,析构时释放
class Res final { public:Res() { sourse = new T(); }~Res() { delete sourse; } private:T* sourse; };
-
智能指针
-
具有独占所有权的
std::unique_ptr<T>
一个对象只能由一个
std::unique_ptr<T>
占有class person { public: private:int age; }; std::unique_ptr<person> p1{ std::make_unique<person>() }; //类型推导更加的简洁 auto p2{ std::make_unique<person>() };
不允许调用
std::unique<T>
拷贝构造函数,但可以使用std::move
将所持有的资源移动给另一个std::unique<T>
,将在后续部分进行讲解 -
具有共享所有权的
std::shared_ptr<T>
std::shared_ptr<T>
可以与其他std::shared_ptr<T>
实例共享资源的所有权std::shared_ptr<T>
提供简单且有限的垃圾回收功能,内部存在一个引用计数器std::shared_ptr<person> s1 = std::make_shared<person>(); std::shared_ptr<person> s2; s2 = std::move(s1);
上面例子并没有修改智能指针计数,但在移动之后必须要小心使用
s1
,因为他可能是空的**(持有nullptr)**3.无所有权但是能安全访问的
std::weak_ptr<T>
他对资源的生命周期没有影响
std::weak_ptr<T>
仅仅观察他所指向的资源
-
-
避免显式的
new
和delete
new和delete会增加代码的复杂度: 当不可避免的使用new和delete时,你必须处理异常情形
显式的调用new和delete可以通过以下措施避免
- 尽可能使用栈内存,栈内存永远不会泄露
- 用
make function
分配内存 - 尽量使用容器
-
管理特有资源
Move语义
什么是move
语义呢?在许多情况下,旧的C++强迫我们使用复制构造函数,实际上我们并不想要对象的深拷贝,相反,我们只移动对象的负载就可以使用std::move
-
左值与右值的关系
左值与右值是历史术语(这个翻译的属实垃圾)
左值更好的解释是:一个locator value,一个在内存有位置的对象
相对于左值,右值是一个不是左值的对象,右值是一个临时对象或者是子对象
字面值 42 是一个右值。在内存中没有可标识的位置,所以不能为他赋值,当然,右值也可以占用栈上的数据区内存,但是这个内存是临时分配的,而且复制完成后就会马上释放
-
右值引用
右值引用使优定位右值的内存成为可能,并且可以修改
int&& temp = 12 + 34; int* p1 = &temp; *p1 = 999;
-
少用
std::move()
编译器自己会优化,自己少用
-
零原则
**缺陷:**违反了五大原则copy/move构造器以及copy/move赋值运算符都没有显示定义
int main()
{Mystring astring("test",4);Mystring anotherstring{astring};return 0;
}
在这个实例中,我们拷贝的对象是存储字符串的地址指针,而不是内存中指针指向的对象。
这就意味着,自动调用默认的复制构造函数后,两个对象指向同一个内存,如果对象析构,将会导致双重删除,会造成严重的后果。
**零原则:**在你实现类的时候,应该不需要声明或定义析构函数,拷贝构造赋值函数,移动构造赋值函数。用C++智能指针来管理资源
面向对象
类的设计原则
我太菜,以后理解到了再写