C++最佳实践之编程建议

C++最佳实践之编程建议,第1张

本文介绍C++的编程建议基于《A Tour of C++》,包括通用指南、结构体、命名空间、异常处理、成员函数、虚函数、构造函数、模版、容器、stl标准库、线程与并发控制。

一、C++通用指南

通用的编程指南建议如下:

  • 保持函数简洁,保持代码简洁;
  • 不要单独使用内置功能或者自己实现;相反,最基本的内置功能最好通过库来调用;
  • 关注编程技术,而不是语言特点;
  • 函数应该执行单个逻辑 *** 作, 不要把多个功能放在同一个函数实现;
  • 当函数在不同类型执行相同任务时,使用函数重载;
  • 如果函数在编译期能确定,那么使用constexpr关键字修饰;
  • 避免复杂表达式;
  • 避免变窄转换,防止精度丢失,比如float类型转为int类型;
  • 变量的作用域最小化,能用局部变量就不要声明为全局变量;
  • 避免魔术常量,使用符号常量;
  • 优先考虑不可变的数据,比如参数不用修改,就声明为const;
  • 避免相似的命名,防止产生歧义;
  • 对于具有命名类型的声明,首选大括号{}来初始化;
  • 使用auto关键字避免重复类型命名;
  • 避免未初始化的变量;
  • 仅在位运算时使用unsigned修饰符;
  • 保持指针的使用简单直接;
  • 使用nullptr,而不用0或者NULL;
  • 能用代码表达,就不用过多注释;
  • 保持一致的缩进样式;
二、结构体、枚举与联合体

关于结构体与枚举的使用建议如下:

  • 结构体是简单类,成员变量默认位public;
  • 使用枚举表示命名常量集合;
  • 区分声明(用作接口)和定义(用作实现);
三、命名空间与异常处理

命名空间用于防止命名冲突、模块隔离。关于命名空间与异常处理建议如下:

  • 使用头文件来表示接口和强调逻辑结构;
  • 源文件使用#include头文件,要实现它声明的函数;
  • 避免在头文件写非内联函数,头文件仅支持内联函数;
  • 不要在头文件使用using命名空间,最小化使用命名空间;
  • 函数执行出错,而且错误影响到调用函数,那么抛出异常;
  • 如果不确定使用异常还是错误码,那么优先使用异常;
  • 能够在编译期检查就在编译期检查,不要等到运行期;
  • 小量数据使用值传递,大量数据使用引用传递;
  • 优先采用常量引用,而不是普通引用;
四、成员函数与虚函数

关于成员函数与虚函数的使用建议如下:

  • 对称运算符使用非成员函数,仅当需要被类直接访问时才设为成员函数;
  • 把成员函数声明为对象状态不可修改;
  • 如果构造函数需要资源,那么它的类需要释构函数来释放资源;
  • 如果类是一个容器,赋予它一个初始化列表的构造函数;
  • 当接口和实现完全分离时,使用抽象类作为接口;
  • 通过指针或引用来访问多态对象;
  • 抽象类不需要构造函数;
  • 有虚函数的类,需要虚的释构函数;
  • 设计类的层次结构时,区分实现继承和接口继承;
  • 使用unique_ptr或share_ptr避免忘记释放new创建的对象;
五、构造函数与类型转换 1、构造函数

构造函数包括默认构造、拷贝构造、移动构造、拷贝赋值构造、移动赋值构造。而释构函数只有一个,并且无参数无返回值。示例代码如下:

class X {
public:
    X(param);               // normal constructor: create an object
    X();                    // default constructor
    X(const X&);            // copy constr uctor
    X(X&&);                 // move constr uctor
    X& operator=(const X&); // copy assignment: clean up target and copy
    X& operator=(X&&);      // move assignment: clean up target and move
    ~X();                   // destructor: clean up
};

对象可以被拷贝或移动有以下五种情况:

  • 作为赋值的来源;
  • 作为对象的初始化;
  • 作为函数参数;
  • 作为函数返回值;
  • 作为一个异常;
2、default默认 *** 作

如果期望某个构造函数作为默认构造,那么使用=default。示例代码如下:

class Y {
public:
    Y(Sometype);
    Y(const Y&) = default; // default copy constructor
    Y(Y&&) = default;      // default move constructor
};
3、delete禁止 *** 作

为了防止调用者不恰当调用,可以在构造函数加上=delete表示禁止某个 *** 作。如果调用=delete修饰的 *** 作,会在编译期报错。示例代码如下:

class Shape {
public:
    Shape(const Shape&) =delete; // no copy operation
    Shape& operator=(const Shape&) =delete; // no assign operation
};

void copy(Shape& s1, const Shape& s2)
{
    s1 = s2; // error: Shape copy is deleted
}
4、单参数的构造函数

默认把单个参数的构造函数声明为explicit,防止被隐式调用。示例代码如下:

class Hello {
public:
    explicit Hello(int a);
}
5、禁止不同类型的拷贝

如果默认值不适用某个类型,禁止拷贝。

六、template模版

为了定义优秀的模版,我们需要遵循以下机制:

  • 数值依赖类型:可变模版;
  • 编译期选择机制:if constexpr;
  • 查询类型和表达式属性的机制;

关于模板的建议如下:

  1. 使用模版表达适用多种参数类型的算法;
  2. 使用模版表达容器;
  3. 使用模版来提升抽象代码的等级;
  4. 让构造函数或函数模板来推断类模板参数类型;
  5. 虚成员函数不能作为模板成员函数;
  6. 使用模板别名来简化表示并隐藏细节;
  7. 模板提供编译期的动态类型;
七、stl标准库

C++提供stl标准库,包括:algorithm、array、vector、map、string、deque、list、thread等。关于stl标准库使用建议如下:

  1. 尽量使用标准库,不要自己造轮子;
  2. 不要认为标准库能使用所有处理情况;
  3. 使用标准库时,前面加std来引用,比如字符串std::string;
  4. 使用substr()来读子字符串,replace()写子字符串;
  5. 当进行范围检查时使用at(),当需要优化速度时使用iterator或[];
  6. 使用c_str()来表示C语言风格的字符串;
八、容器

C++提供的标准容器包括:vector、list、forward_list、deque、set、multiset、map、

multimap、unordered_map、unordered_multimap、unordered_set、unordered_multiset。

关于容器的使用建议如下:

扩容 *** 作使用resize()而不是realloc();

插入 *** 作使用push_back()或者insert(),效率比较高;

map基于红黑树,有序但效率较低。unordered_map基于哈希表,无序但效率较高;

九、线程与并发控制 1、线程初始化

使用函数或结构体作为执行任务,线程初始化的示例代码如下:

void f();  // 函数
struct F { // 函数对象
    void operator();
};

void hello()
{
    thread t1 {f};
    thread t2 {F()};
    t1.join(); // 等待线程执行完毕
    t2.join();
}
2、mutex与lock

多线程并发控制,需要用到mutex互斥锁与lock结合使用,或者condition_variable条件变量。其中,lock有scoped_lock、unique_lock、shared_lock、lock_guard。一般情况下,读数据用shared_lock,写数据用unique_lock()。示例代码如下:

shared_mutex mtx; // 互斥锁
void read_data()
{
    shared_lock lock {mtx};
// 读 *** 作
}
void writer()
{
    unique_lock lock {mtx};
// 写 *** 作
}

关于线程与并发控制的建议如下:

  1. 使用condition_variable条件变量进行多线程通信;
  2. 不要在条件变量情况下等待(容易死锁);
  3. 线程任务使用promise返回结果,从future获取结果;
  4. 使用async()启动简单任务;

至此,C++语言的编程建议介绍完毕。没有严格约束,但是遵循可以提高编程效率,提高代码可读性。如有错漏,欢迎伙伴们指出纠正。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/867109.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-12
下一篇2022-05-12

发表评论

登录后才能评论

评论列表(0条)

    保存