当前位置:  开发笔记 > 编程语言 > 正文

你最喜欢的C++ Coding Style成语是什么?

如何解决《你最喜欢的C++CodingStyle成语是什么?》经验,为你挑选了13个好方法。

你最喜欢的C++编码风格是什么?我问的是样式或编码排版,例如你在哪里放大括号,在关键字后面有空格,缩进的大小等等.这与最佳实践或要求相反,例如总是删除数组delete[].

以下是我最喜欢的一个示例:在C++类初始值设定项中,我们将分隔符放在行的前面而不是后面.这样可以更容易地保持最新状态.这也意味着版本之间的源代码控制差异更清晰.

TextFileProcessor::
TextFileProcessor( class ConstStringFinder& theConstStringFinder ) 

    : TextFileProcessor_Base( theConstStringFinder )

    , m_ThreadHandle  ( NULL )
    , m_startNLSearch (    0 )
    , m_endNLSearch   (    0 )
    , m_LineEndGetIdx (    0 )
    , m_LineEndPutIdx (    0 )
    , m_LineEnds      ( new const void*[ sc_LineEndSize ] )
{
    ;
}

jalf.. 65

RAII:资源获取是初始化

RAII可能是最重要的习语.我们认为资源应该映射到对象,以便根据声明这些对象的范围自动管理它们的生命周期.

例如,如果在堆栈上声明了文件句柄,那么一旦我们从函数返回(或循环,或者在其中声明的范围),它应该被隐式关闭.如果动态内存分配被分配为类的成员,则应该在销毁该类实例时隐式释放它.等等.每种资源内存分配,文件句柄,数据库连接,套接字以及必须获取和释放的任何其他类型的资源都应该包含在这样的RAII类中,其生命周期取决于它的范围.声明.

这样做的一个主要优点是C++保证在对象超出范围时调用析构函数,而不管控件是如何离开该范围的.即使抛出异常,所有本地对象也将超出范围,因此它们的相关资源将被清除.

void foo() {
  std::fstream file("bar.txt"); // open a file "bar.txt"
  if (rand() % 2) {
    // if this exception is thrown, we leave the function, and so
    // file's destructor is called, which closes the file handle.
    throw std::exception();
  }
  // if the exception is not called, we leave the function normally, and so
  // again, file's destructor is called, which closes the file handle.
}

无论我们如何离开函数,以及文件打开后发生的事情,我们都不需要显式关闭文件,或者在该函数中处理异常(例如try-finally).相反,文件被清理,因为它绑定到一个本地对象,当它超出范围时会被销毁.

RAII也不太常见,称为SBRM(范围限制资源管理).

也可以看看:

ScopeGuard允许代码"在抛出异常时自动调用'撤消'操作.."


kshahar.. 59

创建枚举时,将它们放在命名空间中,以便您可以使用有意义的名称访问它们:

namespace EntityType {
    enum Enum {
        Ground = 0,
        Human,
        Aerial,
        Total
    };
}

void foo(EntityType::Enum entityType)
{
    if (entityType == EntityType::Ground) {
        /*code*/
    }
}

编辑:但是,这种技术在C++ 11中已经过时了.应该使用范围枚举(用enum class或声明enum struct):它更加类型安全,简洁,灵活.对于旧式枚举,值将放在外部范围中.使用新式枚举,它们位于enum class名称的范围内.
上一个示例使用作用域枚举(也称为强类型枚举)重写:

enum class EntityType {
    Ground = 0,
    Human,
    Aerial,
    Total
};

void foo(EntityType entityType)
{
    if (entityType == EntityType::Ground) {
        /*code*/
    }
}

使用作用域枚举还有其他显着的好处:缺少隐式转换,可能的前向声明以及使用自定义基础类型(不是默认值int)的能力.



1> jalf..:

RAII:资源获取是初始化

RAII可能是最重要的习语.我们认为资源应该映射到对象,以便根据声明这些对象的范围自动管理它们的生命周期.

例如,如果在堆栈上声明了文件句柄,那么一旦我们从函数返回(或循环,或者在其中声明的范围),它应该被隐式关闭.如果动态内存分配被分配为类的成员,则应该在销毁该类实例时隐式释放它.等等.每种资源内存分配,文件句柄,数据库连接,套接字以及必须获取和释放的任何其他类型的资源都应该包含在这样的RAII类中,其生命周期取决于它的范围.声明.

这样做的一个主要优点是C++保证在对象超出范围时调用析构函数,而不管控件是如何离开该范围的.即使抛出异常,所有本地对象也将超出范围,因此它们的相关资源将被清除.

void foo() {
  std::fstream file("bar.txt"); // open a file "bar.txt"
  if (rand() % 2) {
    // if this exception is thrown, we leave the function, and so
    // file's destructor is called, which closes the file handle.
    throw std::exception();
  }
  // if the exception is not called, we leave the function normally, and so
  // again, file's destructor is called, which closes the file handle.
}

无论我们如何离开函数,以及文件打开后发生的事情,我们都不需要显式关闭文件,或者在该函数中处理异常(例如try-finally).相反,文件被清理,因为它绑定到一个本地对象,当它超出范围时会被销毁.

RAII也不太常见,称为SBRM(范围限制资源管理).

也可以看看:

ScopeGuard允许代码"在抛出异常时自动调用'撤消'操作.."



2> kshahar..:

创建枚举时,将它们放在命名空间中,以便您可以使用有意义的名称访问它们:

namespace EntityType {
    enum Enum {
        Ground = 0,
        Human,
        Aerial,
        Total
    };
}

void foo(EntityType::Enum entityType)
{
    if (entityType == EntityType::Ground) {
        /*code*/
    }
}

编辑:但是,这种技术在C++ 11中已经过时了.应该使用范围枚举(用enum class或声明enum struct):它更加类型安全,简洁,灵活.对于旧式枚举,值将放在外部范围中.使用新式枚举,它们位于enum class名称的范围内.
上一个示例使用作用域枚举(也称为强类型枚举)重写:

enum class EntityType {
    Ground = 0,
    Human,
    Aerial,
    Total
};

void foo(EntityType entityType)
{
    if (entityType == EntityType::Ground) {
        /*code*/
    }
}

使用作用域枚举还有其他显着的好处:缺少隐式转换,可能的前向声明以及使用自定义基础类型(不是默认值int)的能力.


如何为这样的问题"接受"答案?没有废话给kshahar,这是一个很好的技巧,但"接受"一个答案会自动将它带到顶端并且不会让一个更民主的评估过程发生,这对于"你最喜欢的......"是一种耻辱.题
现在是强类型枚举的时间:)
我同意这一点,除了我通常把它作为课程.(这是因为我有一些代码生成,可以让我自动执行枚举到字符串和字符串到枚举的转换.)

3> RC...:

复制交换

复制交换习惯用法提供了异常安全的复制.它要求实现正确的复制ctor和swap.

struct String {
  String(String const& other);

  String& operator=(String copy) { // passed by value
    copy.swap(*this); // nothrow swap
    return *this; // old resources now in copy, released in its dtor
  }

  void swap(String& other) throw() {
    using std::swap; // enable ADL, defaulting to std::swap
    swap(data_members, other.data_members);
  }

private:
  Various data_members;
};
void swap(String& a, String& b) { // provide non-member for ADL
  a.swap(b);
}

您还可以直接使用ADL(Argument Dependent Lookup)实现swap方法.

这个成语很重要,因为它处理自我赋值[1],强烈的异常保证[2],并且通常很容易编写.


[1]即使自我分配没有尽可能有效地处理,它应该是罕见的,所以如果它永远不会发生,这实际上更快.

[2]如果抛出任何异常,*this则不修改object()的状态.


有没有理由"使用std :: swap;" 而不只是"std :: swap(data_members,other.data_members);"?
leetNightshade:这样,如果一个类成员也使用了复制和交换习惯用法,那么依赖于参数的查找应该调用优化程度更好的交换函数.std :: swap通常会调用一个复制构造函数(在C++ 11 +中移动构造函数)和两个赋值运算符,而对于copy-and-swap,每个赋值运算符都会调用特定于类的交换函数.因此,使用std :: swap会花费两倍的成本,加上移动或复制构造函数的成本.这并不是说copy-and-swap使std :: swap超慢; 专业版更好.

4> 小智..:

CRTP:奇怪的重复模板模式

将类作为模板参数传递给其基类时会发生CRTP:

template
struct BaseCRTP {};

struct Example : BaseCRTP {};

在基类中,只需通过强制转换(static_castdynamic_cast工作),它就可以得到派生实例的ahold,完成派生类型:

template
struct BaseCRTP {
  void call_foo() {
    Derived& self = *static_cast(this);
    self.foo();
  }
};

struct Example : BaseCRTP {
  void foo() { cout << "foo()\n"; }
};

实际上,call_foo已经注入到派生类中,可以完全访问派生类的成员.

随意编辑和添加特定的使用示例,可​​能是其他SO帖子.


如果一个成语有一个坏名字(并且C++有一些lulus)那么这就必须如此.我认为你应该至少描述这个答案的一个好奇用法是有用的.
它被称为Curious_ly_重复模板模式.这是一个副词:复发是好奇的,而不是模式:-)
@Nils表演!`self.foo`被静态调度到`Example`,而`virtual`方法调用将动态调度,如果你对微优化很迂腐,****会慢一些!

5> jalf..:

pImpl:指向实现的指针

pImpl习惯用法是将类的接口与其实现分离的非常有用的方法.

通常,类定义必须包含成员变量和方法,这可能会暴露太多信息.例如,成员变量可以是标题中定义的类型,我们不希望包含在任何地方.

windows.h标题是这里最好的例子.我们可能希望HANDLE在一个类中包含一个或另一个Win32类型,但是我们不能HANDLE在类定义中放入一个而不必包含windows.h所使用的类的所有地方.

然后解决方案是创建类的P rivate IMPL ementationP ointer-to- IMPL ementation,并让public实现只存储指向私有的指针,并转发所有成员方法.

例如:

class private_foo; // a forward declaration a pointer may be used

// foo.h
class foo {
public:
  foo();
  ~foo();
  void bar();
private:
  private_foo* pImpl;
};

// foo.cpp
#include whichever header defines the types T and U

// define the private implementation class
class private_foo {
public:
  void bar() { /*...*/ }

private:
  T member1;
  U member2;
};

// fill in the public interface function definitions:
foo::foo() : pImpl(new private_foo()) {}
foo::~foo() { delete pImpl; }
void foo::bar() { pImpl->bar(); }

foo现在,它的实现与其公共接口分离,因此

它可以使用其他头文件中的成员和类型,而不需要在使用类时存在这些依赖关系,并且

可以在不强制重新编译使用该类的代码的情况下修改实现.

该类的用户只需包含标题,该标题不包含有关该类实现的任何特定内容.所有实现细节都包含在里面foo.cpp.


rivate IMPL

ointer-to-
,并让public实现只存储指向私有的指针,并转发所有成员方法.

6> Prembo..:

我喜欢在'列'中排列代码/初始化...在使用'column'模式编辑器进行编辑时证明非常有用,而且对我来说似乎也更容易阅读...

int myVar        = 1;    // comment 1
int myLongerVar  = 200;  // comment 2

MyStruct arrayOfMyStruct[] = 
{   
    // Name,                 timeout,   valid
    {"A string",             1000,      true    },   // Comment 1
    {"Another string",       2000,      false   },   // Comment 2 
    {"Yet another string",   11111000,  false   },   // Comment 3
    {NULL,                   5,         true    },   // Comment 4
};

相比之下,相同的代码没有缩进和格式如上所示......(有点难以读懂我的眼睛)

int myVar = 1; // comment 1
int myLongerVar = 200; // comment 2

MyStruct arrayOfMyStruct[] = 
{   
    // Name, timeout, valid
    {"A string", 1000, true},// Comment 1
    {"Another string", 2000, false }, // Comment 2 
    {"Yet another string", 11111000,false}, // Comment 3
    {NULL, 5, true }, // Comment 4
};


这个问题是,当一行变得太长时,(说你必须添加"又一个字符串,但这个字符串比其余字符串长"),你要么必须打破好的模式,要么重新格式化所有内容,导致到每个线路都在变化的源控制噩梦.

7> Sebastian Ma..:

Public Top - Private Down

这是一个看似很小的优化,但自从我改用这个大会以来,我有更多的时间来掌握我的课程,特别是在我42年没有看过它们之后.

拥有一致的成员可见性,从经常感兴趣的点到无聊的东西,是非常有帮助的,特别是当代码应该是自我记录时.

(qt-users的旁注:插槽位于信号之前,因为它们应该像非插槽成员函数一样可调用,除了它们的插槽与非插槽无法区分)

公共,受保护,私人

然后工厂,ctor,dtor,复制,交换

然后是类的接口最后,在一个单独的private:部分,来自数据(理想情况下只是一个impl指针).

如果您在保持班级声明整洁时遇到问题,此规则也会有所帮助.

class Widget : public Purple {
public:
    // Factory methods.
    Widget FromRadians (float);
    Widget FromDegrees (float);

    // Ctors, rule of three, swap
    Widget();
    Widget (Widget const&);
    Widget &operator = (Widget const &);
    void swap (Widget &) throw();

    // Member methods.
    float area() const;

    // in case of qt {{ 
public slots:
    void invalidateBlackHole();

signals:
    void areaChanged (float);
    // }}

protected:    
    // same as public, but for protected members


private:    
    // same as public, but for private members

private:
    // data
    float widgetness_;
    bool  isMale_;
};



8> zerbp..:

if语句中,当条件困难时,您可以清楚地显示每个条件使用缩进的级别.

if (  (  (var1A == var2A)
      || (var1B == var2B))
   && (  (var1C == var2C)
      || (var1D == var2D)))
{
   // do something
}


如果这是我的代码,我会把两个|| 在同一行上的条件,但是你已经完成了&&条件的分割,产生了一个2行if语句.
拼写错误和拼写错误也会在我工作的代码中得到纠正!

9> ididak..:

没有收藏,但我修复具有以下代码:

    选项卡 - 导致许多IDE和代码审查工具中的错位,因为它们并不总是在mod 8空格的选项卡上达成一致.

    超过80列的行 - 让我们面对它,更短的行更具可读性.只要线条很短,我的大脑就可以解析大多数编码约定.

    带有尾随空格的行 - git会抱怨它为空格错误,它在差异中显示为红色斑点,这很烦人.

这是一个单行找到有问题的文件:

git grep -I -E '|.{81,}|  *$' | cut -f1 -d: | sort -u

tab字符在哪里(POSIX regexp不做\ t)


好的TAB字符应该用于缩进,而不是格式化.应使用空格进行格式化.这样,代码将很好地显示任何TAB宽度设置.无论如何,TAB应该是8个字符.

10> 小智..:

re:ididak

我修复了将长语句分成太多短语的代码.

让我们面对现实:不再是90年代了.如果你的公司买不起宽屏液晶显示器,你需要得到更好的工作:)


我在宽屏幕上并排放置了多个窗户.我希望能够在笔记本电脑上(读取)代码.
你得到我的+1票.有时包装长行确实有帮助,但是因为它击中80列标记而自动换行只是愚蠢的.
即使你的显示器一直缠绕在头上,你也可以更容易地接受更短的线条.让代码上下文确定线条是否需要很长; 通常它没有.

11> seh..:

编译时多态性

(也称为句法多态和静态多态,与运行时多态性形成对比.)

使用模板函数,可以编写依赖于类型构造函数和调用参数化类型族的签名的代码,而无需引入公共基类.

在" 编程元素 "一书中,作者将这种类型的处理称为抽象属.使用概念,可以指定对此类型参数的要求,但C++不要求这样的规范.

两个简单的例子:

#include 

template 
T twice(T n) {
  return 2 * n;
}

InIt find(InIt f, InIt l,
          typename std::iterator_traits::reference v)
{
  while (f != l && *f != v)
    ++f;
  return f;
}   

int main(int argc, char* argv[]) {
  if (6 != twice(3))
    throw std::logic_error("3 x 2 = 6");

  int const nums[] = { 1, 2, 3 };
  if (nums + 4 != find(nums, nums + 4, 42))
    throw std::logic_error("42 should not have been found.");

  return 0;
}

可以twice使用任何*定义了二元运算符的常规类型调用.类似地,可以调用find()任何可比较的类型和输入迭代器模型.一组代码在不同类型上操作类似,没有共享基类.

当然,这里真正发生的是在模板实例化时将相同的源代码扩展为各种类型特定的函数,每个函数都有单独生成的机器代码.容纳不带模板的相同类型的集合将需要1)具有特定签名的单独的手写函数,或2)通过虚函数的运行时多态性.



12> epatel..:

模板和钩子

这是一种在框架中尽可能多地处理的方法,并为框架的用户提供定制的钩子.也称为热点和模板方法.

class Class {
  void PrintInvoice();     // Called Template (boilerplate) which uses CalcRate()
  virtual void CalcRate() = 0;  // Called Hook
}

class SubClass : public Class {
  virtual void CalcRate();      // Customized method
}

Wolfgang Pree在他的" 面向对象软件开发的设计模式"一书中描述.



13> franji1..:

如果/同时/用于带空格分隔符的带括号的表达式

if (expression)  // preferred - if keyword sticks out more

if(expression)  // looks too much like a void function call

我想这暗示着我喜欢我的函数调用没有空格分隔符

foo(parm1, parm2);


无论价格多少,[llvm编码标准](http://llvm.org/docs/CodingStandards.html#micro_spaceparen)都同意您的意见,但将其列在“微观细节”部分。
推荐阅读
臭小子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有