C++ 是什么? 您从经验中学到的相关习语、误解和陷阱?

发布于 2024-07-09 22:09:09 字数 423 浏览 7 评论 0原文

您从经验中学到了哪些与 C++ 相关的习惯用法、误解和陷阱?

一个例子:

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

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

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

};

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

这意味着 p 不能指向其他内容。并不是说你不能更改p 的数据。

What are some C++ related idioms, misconceptions, and gotchas that you've learnt from experience?

An example:

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

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

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

};

Even know changeS is a const member function, it is changing the value of the object. So a const member function only means that it will treat all variables as const, and it does not mean that it will actually keep all members const. (why? the const keyword on the member function treats char *p; as char * const p; And not as const char *p;

Which therefore means that p can't point to something else. And not that you can't change p's data.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

や莫失莫忘 2024-07-16 22:09:09

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

快,描述一下这个 typedef:

typedef C &(__cdecl C::* const CB )(const C &) const;

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

C++ 函数声明规范语法是出了名的迟钝且难以记住。 是的,经验丰富的 C++ 老手可能会使用一些技巧来解读这种恐怖,但这不是本技巧的内容。 这篇技巧是关于如何不需要记住这种可怕的语法,并且仍然能够声明这样的函数指针 typedef(例如,如果您正在与一些从未听说过 boost::function 的遗留 API 进行交互)。
让编译器为您完成工作,而不是费尽心思。 下次当你尝试为成员函数创建一个 typedef 时,如下所示:

struct C {
        const C& Callback(const C&) const   { }
};

与其费力地手动想出上面的复杂语法,不如引入一个故意的编译错误,这将迫使编译器命名这个野兽。

例如:

char c = &C::Callback;

编译器很高兴地发出这个有用的错误消息:

“… cannot convert from 'const C &(__cdecl C::* )(const C &) const' to 'char'”

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

You don't need to know C++'s complicated function typedef declaration syntax. Here's a cute trick I found.

Quick, describe this typedef:

typedef C &(__cdecl C::* const CB )(const C &) const;

Easy! CB is a pointer to a member function of class C accepting a const reference to a C object and returning a non-const reference to a C object. Oh, and it’s a const member function. Oh, and the function pointer itself is const… (Right?)

The C++ function declaration specification syntax is notoriously obtuse and hard to remember. Yes, there are tricks seasoned C++ veterans may use to decipher such horrors, but that’s not what this tip is about. This tip is about how you don’t need to remember this horrible syntax and still be able to declare such function pointer typedefs (e.g. in case you’re interacting with some legacy API that never heard of boost::function).
Instead of breaking a mental sweat, let the compiler do the work for you. Next time you’re trying to create a typedef to a member function that looks like this:

struct C {
        const C& Callback(const C&) const   { }
};

Instead of struggling to manually come up with the complex syntax above, induce an intentional compilation error which will force the compiler to name the beast.

For example:

char c = &C::Callback;

The compiler happily spews this helpful error message:

“… cannot convert from 'const C &(__cdecl C::* )(const C &) const' to 'char'”

Which is what we’re looking for. :)

时间你老了 2024-07-16 22:09:09

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

assert(condition || !"Something has gone wrong!");

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

assert(!"Something has gone wrong!");

以下内容归因于 @Josh(参见评论)。 它使用 逗号运算符 代替:

assert(("Something has gone wrong!", condition)); 

I've liked this since the time i've discovered it in some code:

assert(condition || !"Something has gone wrong!");

or if you don't have a condition at hand, you can just do

assert(!"Something has gone wrong!");

The following is attributed to @Josh (see comments). It uses the comma operator instead:

assert(("Something has gone wrong!", condition)); 
又怨 2024-07-16 22:09:09

这是我有一天发现的另一个:

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

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

Here is another one i caught some day:

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

it's just indexing the char array instead of doing a switch. If it's outside the range, it returns '-'.

送君千里 2024-07-16 22:09:09

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

Never waste time on trying to implement the copy operations on classes when we don't know if it will be required later. Many objects we handle are just entities, and copying them hardly make any sense. Make them non-copyable, and implement the copy/duplication later if the need really arises.

我一直都在从未离去 2024-07-16 22:09:09

有时,标头会被不具有行为的宏名称所污染,例如

#define max(a, b) (a > b ? a : b)

“这将使使用 max 函数或以这种方式调用的函数对象的代码无效”。 一个臭名昭著的例子是windows.h,它正是这样做的。 解决这个问题的一种方法是在调用周围加上括号,这会阻止它使用宏并使其使用真正的 max 函数:

void myfunction() {
    ....
    (max)(c, d);
}

现在,最大值位于括号中,并且不再算作对宏的调用!

Sometimes, headers are polluted with not behaving macro names like

#define max(a, b) (a > b ? a : b)

Which will render code invalid that uses a max function or function object called that way. An infamous example is windows.h which does exactly that. One way around it is putting parentheses around the call, which stops it from using the macro and makes it use the real max function:

void myfunction() {
    ....
    (max)(c, d);
}

Now, the max is in parentheses and it is not counted as a call to the macro anymore!

妄司 2024-07-16 22:09:09

一种很少使用但很方便的 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; }
}  

C++ 不允许在构造函数体内更改 const 值,因此这可以避免 const 强制转换。

One seldom used, but handy C++ idiom is the use of the ?: operator during the constructor chain.

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; }
}  

C++ doesn't allow const values to be changed inside the body of the constructor, so this avoids const-casts.

请持续率性 2024-07-16 22:09:09

您通常可以在源文件中隐藏比您想象的更多的内容。 如果没有必要,不要将所有内容设为私有 - 通常最好将其保留在源文件中的匿名名称空间中。 我发现,它实际上使事情更容易处理,因为这样你就不会透露实现细节,但会受到启发来制作许多微小的函数而不是单一的函数。

You can often hide way more stuff in source files than you think. Don't make everything private if you don't have to - it's often better to leave it in an anonymous namespace in the source file. It actually makes things easier to process, I find, because then you aren't revealing implementation details, yet get inspired to make lots of tiny functions rather than monolithic ones.

踏雪无痕 2024-07-16 22:09:09

一些通常会让人困惑的事情:

std::cout << a << a++ << --a;
i = ++i;

上面的行都是未定义的。

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

int main() {
  foo(shared_ptr<bar>(new bar()), shared_ptr<bar>(new bar()));
}

以上可能会泄漏内存。

int* arr = new int[10];
arr + 11;

这会导致未定义的行为。

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

A few things that usually trip people up:

std::cout << a << a++ << --a;
i = ++i;

The above lines are both undefined.

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

int main() {
  foo(shared_ptr<bar>(new bar()), shared_ptr<bar>(new bar()));
}

The above may leak memory.

int* arr = new int[10];
arr + 11;

This results in undefined behavior.

As for idioms, my favorite is RAII. Allocate objects on the stack, which guarantees that the destructor is called when the object goes out of scope, preventing resource leaks.

三月梨花 2024-07-16 22:09:09

因为我们都忽略了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;
};

Since we're all ignoring the OP and instead posting our favourite cool tricks...

Use boost (or tr1) shared_ptr to maintain a class invariant at runtime (kind of obvious, but I haven't seen anyone else do it):

#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;
};
日久见人心 2024-07-16 22:09:09

自从我了解 RAII(有史以来最糟糕的缩写之一)和智能指针以来,内存和资源泄漏几乎完全消失了。

Since I learned about RAII (one of the worst acronyms ever) and smart pointer, memory and resource leaks have almost completely disappeared.

甜点 2024-07-16 22:09:09

如果您有一个没有值语义的类,请确保显式声明以下所有构造,以防止日后出现麻烦。

  • 默认构造函数
  • 复制构造函数
  • 赋值运算符

在许多情况下,您只需要声明这些构造的子集。 然而,在某些情况下,哪些是需要的,哪些是不需要的,这可能会变得非常棘手。 将这三个事件全部宣布为私有并解决这件事会更安全。

在顶部添加注释解释这不是一个复制安全的类也非常有帮助。

节省您的时间。

If you have a class which does not have value semantics, make sure that all of the following constructs are explicitly declared in order to prevent headaches down the road.

  • Default Constructor
  • Copy Constructor
  • Assignment Operator

In many cases you only need to declare a subset of these constructs. However it can get really tricky in some cases as to which are needed and which are not. It's much safer to declare all 3 private and be done with the matter.

It's also very helpful to add a comment to the top explaining that this is not a copy safe class.

This will save you time down the road.

蓝眼睛不忧郁 2024-07-16 22:09:09

我不能说我是一名经验丰富的 C++ 程序员,但我最近了解到将数组数组作为函数参数传递是多么困难。 尽量避免这种情况:(

如果你知道编译时的大小,那就很简单了。即使你在编译时知道其中一个维度。
如果您根本不知道...您可能会看到类似这样的内容

m[i*dim2+j]

:i 是行的迭代器,dim2 是列的数量,j 是列的迭代器。

I can't say that I am an experienced C++ programmer but I have recently learned how hard it is to pass and array of arrays as a function parameter. Try to avoid this at all cost :(

If you know the size at compile its simple. Even if you know one of the dimensions at compile time.
If you simply don't know... you might be looking at something like this

m[i*dim2+j]

Being i the iterator for the rows, dim2 the number of cols and j the iterator for the cols.

勿挽旧人 2024-07-16 22:09:09

我在编程时实际上没有遇到任何问题,但一位朋友想要解释代码为何有效。 我花了一段时间才弄清楚。 也许这对你们来说是显而易见的,但我不是一个经验丰富的程序员。

#include <iostream>
using namespace std;

int& strangeFunction(int& x){return x;}


int main(){
        int a=0;
        strangeFunction(a) = 5;               //<------- I found this very confusing
        cout << a <<endl;
        return 0;
}

Nothing I actually came across while programming, but a friend wanted an explanation on why the code worked. It took me a while to figure it out. Maybe it is obvious to you guys, but I am not an experienced programmer.

#include <iostream>
using namespace std;

int& strangeFunction(int& x){return x;}


int main(){
        int a=0;
        strangeFunction(a) = 5;               //<------- I found this very confusing
        cout << a <<endl;
        return 0;
}
标点 2024-07-16 22:09:09

除非需要,否则不要使用 shared_ptr。 更喜欢使用 C++ 引用和 unique_ptrshared_ptr 是一个性能消耗者,使代码看起来比实际更复杂。 通常这不会是一个问题,除了 shared_ptr 异常的吸引力及其传染性。

Do not prefer to use shared_ptr unless needed. Prefer using C++ references and unique_ptr instead. shared_ptr is a performance hog and makes the code look more complex than it is. Normally this won't be an issue, except for the unusual attractiveness of shared_ptr and its contagious nature.

奶茶白久 2024-07-16 22:09:09

使用 boost::spirit::hold_any (link) 而不是 boost::any 以获得性能代码(同时容纳大量小对象)。 我看到他们的表现有很大的差异。

Use boost::spirit::hold_any (link) instead of boost::any for performance code (while holding a large number of small objects). I saw a large difference in their performances.

誰認得朕 2024-07-16 22:09:09

除非需要,否则不要陷入使用 std::noncopyable 的陷阱。 是的,它在很多地方都很有用,并且应该在那里使用。

陷阱是,人们开始编写 clone() 函数并使其不可复制,这实现了相同的功能。 相反,您也可以使用显式链接) 用于复制构造函数,以防止意外复制(并将赋值设为私有,或删除 C++0x 中的函数)。 不过,继承的基类需要 clone()

Do not fall into the trap of using std::noncopyable unless needed. Yes, it is useful at a lot of places, and should be used there.

The trap is that one starts writing clone() function along with making it noncopyable, which implements the same functionality. Instead, you can also use explicit (link) for the copy constructor instead to prevent accidental copying (and make assignment private, or delete the function in C++0x). clone() is needed for inherited base classes though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文