您从经验中学到了哪些与C++相关的习语,误解和陷阱?

Bri*_*ndy 27 c++ idioms

您从经验中学到了哪些与C++相关的习语,误解和陷阱?

一个例子:

class A
{
  public: 
  char s[1024];
  char *p;

  A::A()
  {
    p = s;
  }

  void changeS() const
  {
    p[0] = 'a';
  }

};
Run Code Online (Sandbox Code Playgroud)

即使知道changeS是一个const成员函数,它也在改变对象的值.因此const成员函数只意味着它将所有变量视为const,并不意味着它实际上将所有成员保持为const.(为什么?成员函数上的const关键字将char*p视为char*const p;而不是const char*p;

因此,这意味着p不能指向别的东西.而不是你不能改变p的数据.

Ass*_*vie 55

您不需要了解C++的复杂函数typedef声明语法.这是我发现的一个可爱的伎俩.

快速,描述这个typedef:

typedef C &(__cdecl C::* const CB )(const C &) const;
Run Code Online (Sandbox Code Playgroud)

简单!CB是指向C类成员函数的指针,它接受对C对象的const引用并返回对C对象的非const引用.哦,这是一个const成员函数.哦,函数指针本身就是const ...(对吧?)

众所周知,C++函数声明规范语法是钝的,难以记住.是的,经验丰富的C++老手可能会使用这些技巧来破译这样的恐怖事件,但这不是这个提示的内容.这个技巧是关于你如何不需要记住这个可怕的语法,并且仍然能够声明这样的函数指针typedef(例如,如果你正在与一些从未听说过boost :: function的遗留API进行交互).让编译器为您完成工作,而不是打破精神上的汗水.下次你尝试为成员函数创建一个typedef,如下所示:

struct C {
        const C& Callback(const C&) const   { }
};
Run Code Online (Sandbox Code Playgroud)

而不是努力手动提出上面的复杂语法,诱导有意的编译错误,这将迫使编译器命名为野兽.

例如:

char c = &C::Callback;
Run Code Online (Sandbox Code Playgroud)

编译器愉快地吐出这个有用的错误消息:

“… cannot convert from 'const C &(__cdecl C::* )(const C &) const' to 'char'”
Run Code Online (Sandbox Code Playgroud)

这就是我们正在寻找的.:)


Joh*_*itb 18

自从我在一些代码中发现它以来,我就喜欢这个:

assert(condition || !"Something has gone wrong!");
Run Code Online (Sandbox Code Playgroud)

或者如果你手边没有条件,你可以这样做

assert(!"Something has gone wrong!");
Run Code Online (Sandbox Code Playgroud)

以下是@Josh(见评论).它使用逗号运算符代替:

assert(("Something has gone wrong!", condition)); 
Run Code Online (Sandbox Code Playgroud)

  • 为什么双重否定?(`assert(c &&"expected c")`) (3认同)

Joh*_*itb 18

这是我有一天抓到的另一个:

char int2hex(int x) {
     return "-0123456789abcdef"[(x >= 0 && x < 16) ? (x + 1) : 0];
}
Run Code Online (Sandbox Code Playgroud)

它只是索引char数组而不是进行切换.如果它超出范围,则返回" - ".

  • 为什么不写'((x> = 0 && x <16)?(x + 1):0)[" - 0123456789abcdef"]'以获得最大的陌生感?:) (16认同)
  • 我会在最后推到' - '并以......结束?x:16]; (4认同)
  • 最初他们只做了"0123456789abcdef"[x].我不想要ppl认为我鼓励缓冲区溢出:p (2认同)

Luc*_*tte 14

当我们不知道以后是否需要它时,不要浪费时间尝试在类上实现复制操作.我们处理的许多对象只是实体,复制它们几乎没有任何意义.使它们不可复制,如果确实需要,稍后实施复制/复制.

  • 如Scott Meyers在"Effective C++"第3版中所建议的那样,从"uncopyable"类继承(私下). - 第6项是一个很好的,简单的方法. (2认同)

Joh*_*itb 11

有时候,标题会被污染,而不会出现像宏名称那样的宏名称

#define max(a, b) (a > b ? a : b)
Run Code Online (Sandbox Code Playgroud)

这将使代码无效,使用以这种方式调用的max函数或函数对象.一个臭名昭着的例子windows.h正是如此.绕过它的一种方法是在调用周围加上括号,这会阻止它使用宏并使其使用真正的最大函数:

void myfunction() {
    ....
    (max)(c, d);
}
Run Code Online (Sandbox Code Playgroud)

现在,最大值在括号中,它不再被视为对宏的调用了!

  • 拉屎.(为什么windows.h还需要定义它?) (4认同)

Pro*_*ack 7

很少使用,但方便的C++习惯用法是在构造函数链中使用?:运算符.

class Sample
{  
    const char * ptr;
    const bool  freeable;

    Sample(const char * optional):
        ptr( optional ? optional : new char [32]),
        freeable( optional ? false : true ) {}
    ~Sample( )  { if (freeable) delete[] ptr; }
}  
Run Code Online (Sandbox Code Playgroud)

C++不允许在构造函数体内更改const值,因此这避免了const-casts.

  • `freeable(可选== 0)` (5认同)

cop*_*pro 6

您可以经常隐藏源文件中的更多内容而不是您想象的内容.如果不需要,请不要将所有内容设为私有 - 通常最好将其保留在源文件中的匿名命名空间中.事实上,它实际上使得处理起来更容易,因为那时你并没有透露实现细节,而是获得灵感来制作许多微小的功能而不是单片功能.


jal*_*alf 5

一些通常会让人兴奋的事情:

std::cout << a << a++ << --a;
i = ++i;
Run Code Online (Sandbox Code Playgroud)

以上几行都是未定义的.

void foo(bar* b1, bar* b2);

int main() {
  foo(shared_ptr<bar>(new bar()), shared_ptr<bar>(new bar()));
}
Run Code Online (Sandbox Code Playgroud)

以上可能会泄漏内存.

int* arr = new int[10];
arr + 11;
Run Code Online (Sandbox Code Playgroud)

这导致未定义的行为.

至于成语,我最喜欢的是RAII.在堆栈上分配对象,这可以保证在对象超出范围时调用析构函数,从而防止资源泄漏.

  • 需要明确的是:`arr + 10`是明确定义且合法的,但不能被解除引用; `arr + 11`未定义. (3认同)

fiz*_*zer 5

因为我们都忽略了OP而是发布了我们最喜欢的酷炫技巧......

使用boost(或tr1)shared_ptr在运行时维护类不变量(显而易见,但我还没有看到其他人这样做):

#include <cassert>
#include <functional>
#include <stdexcept>
#include <boost/shared_ptr.hpp>
using namespace std;
using namespace boost;

class Foo
{
public:
    Foo() : even(0)
    {
        // Check on start up...
        Invariant();
    }

    void BrokenFunc()
    {
        // ...and on exit from public non-const member functions.
        // Any more is wasteful.
        shared_ptr<Foo> checker(this, mem_fun(&Foo::Invariant));

        even += 1;
        throw runtime_error("didn't expect this!");
        even += 1;
    }

private:
    void Invariant() { assert(even % 2 == 0); }
    int even;
};
Run Code Online (Sandbox Code Playgroud)

  • 有趣.我想我会声明一个名为"Invariant"的朋友子类,其中包含ctor和dtor中的检查,并且在每个公共成员函数的开头都在堆栈上声明了该实例.您的代码稍微短一点,但它只在函数出口处检查. (2认同)
  • 我知道了.好吧,你可能想看一下Loki的ScopeGuard,它做了很多相同的事情,但没有shared_ptr做的动态内存分配:你只需调用"ON_BLOCK_EXIT(&Foo :: Invariant);" 链接:http://loki-lib.sourceforge.net/index.php?n = Idom.ScopeGuardPointer (2认同)