巧妙破解C ++代码的邪恶样本

时间:2010-11-28 06:23:40

标签: c++

我需要一些糟糕的C ++代码示例来说明违反良好做法的行为。我想提出我自己的例子,但是我很难想出那些没有做作的例子,而且陷阱不是很明显(它比看起来更难)。< / p>

示例如下:

  1. 不为具有std::auto_ptr成员的类定义复制构造函数,并使用具有前向声明类的std::auto_ptr成员。
  2. 从构造函数或析构函数(直接或间接)调用虚函数。
  3. 重载模板功能。
  4. 使用boost::shared_ptr的循环引用。
  5. 切片。
  6. 从C回调中抛出异常(直接或间接)。
  7. 相等的浮点比较。
  8. 具有原始指针成员的构造函数的异常安全性。
  9. 从析构者那里投掷。
  10. 在不同体系结构上编译时出现整数溢出(size_tint不匹配)。
  11. 使容器迭代器失效。
  12. ......或者你能想到的任何其他邪恶的东西。

    我很欣赏对现有资源或一两个样本的一些指示。

8 个答案:

答案 0 :(得分:34)

The most vexing parse是C ++解析这样的事情的一种令人惊讶的违反直觉的结果:

// Declares a function called "myVector" that returns a std::vector<float>.
std::vector<float> myVector(); 
// Does NOT declare an instance of std::vector<float> called "myVector"

// Declares a function called "foo" that returns a Foo and accepts an unnamed
// parameter of type Bar.
Foo foo(Bar()); 
// Does NOT create an instance of Foo called "foo" nor creates a Bar temporary

// Declares a function called "myVector" that takes two parameters, the first named
// "str" and the second unnamed, both of type std::istream_iterator<int>.
std::vector<float> myVector( 
    std::istream_iterator<int>(str),
    std::istream_iterator<int>()
);
// Does NOT create an instance of `std::vector<float>` named "myVector" while copying
// in elements from a range of iterators

这对任何不熟悉这种特殊语言的人(我自己开始学习C ++时都包含在内)都会感到惊讶。

答案 1 :(得分:15)

#include <iostream>

class Base
{
    public:
        virtual void foo() const { std::cout << "A's foo!" << std::endl; }
};

class Derived : public Base
{
    public:
        void foo() { std::cout << "B's foo!" << std::endl; }
};

int main()
{
    Base* o1 = new Base();
    Base* o2 = new Derived();
    Derived* o3 = new Derived();

    o1->foo();
    o2->foo();
    o3->foo();
}

输出是:

A's foo!
A's foo!
B's foo!

不确定它是否有名字,但肯定是邪恶的! :P

答案 2 :(得分:12)

非异常安全的代码可能会以对代码读者不明显的方式失败:

// Order of invocation is undefined in this context according to the C++ standard.
// It's possible to leak a Foo or a Bar depending on the order of evaluation if one
// of the new statements throws an exception before their auto_ptrs can "own" it
accept_two_ptrs(std::auto_ptr<Foo>(new Foo), std::auto_ptr<Bar>(new Bar));

void MyClass::InvokeCallback(CallbackType cb)
{
    Foo* resource = new Foo;
    cb(resource); // If cb throws an exception, resource leaks
    delete resource;
}

答案 3 :(得分:8)

This one came up earlier tonight。正如@ Billy ONeal在该帖子上指出的那样,循环输入流,仅检查eof(),如果流上发生错误,则可能导致无限循环。应该使用good()代替。

<强> BAD:

while( !cin.eof() ) {
   getline(cin, input);
}

确定:

while( cin.good() ) {
   getline(cin, input);
}

[credit:@James McNellis]

<强>◎:

while (std::getline(std::cin, input)) {
}

答案 4 :(得分:7)

您认为该计划会打印什么?

#include <iostream>
using namespace std;

struct A {
    void f(int) { cout << "a" << endl; }
};

struct B: public A {
    void f(bool) { cout << "b" << endl; }
};

int main() {
    B b;
    b.f(true);
    b.f(1);
    A* a = &b;
    a->f(true);
    return 0;
}

答案:bba!第一次打印输出很明显。第二个是b,因为B::f(bool)的定义隐藏了A::f(int)的定义。第三个是a,因为重载解析发生在静态类型上。

(来源:本周大师,但我找不到文章。)

答案 5 :(得分:6)

重载赋值运算符但not handling self-assignment correctly

答案 6 :(得分:5)

大多数C ++程序员都不太了解参数依赖查找(ADL,也称为Koenig查找),并且可能会导致一些非常不寻常的结果,尤其是与模板结合使用时。

我在回答What are the pitfalls of ADL?

时讨论了ADL的一个主要缺陷

重载解析涉及很多复杂性。在命名空间作用域使用指令时经常出现问题,尤其是using namespace std,因为该命名空间有大量具有通用名称的实体。

以下是导致using namespace std问题的最近两个例子:

答案 7 :(得分:2)

这一个,恕我直言,也很棘手:

class Base {
int _value;

public:
    Base() {
        _value = g();
    }

    virtual int f() = 0;

    int g() { return f(); }
};

class Derived: Base {   
public:
    Derived(): Base()
    { /* init Derived */ }

    int f() { /* implementation */ }
}

由于未实现纯虚方法f(),您的代码将崩溃。显而易见的原因是Derived在构造函数中尚未完成,因此您最终将调用虚拟纯f()并且不会被编译器检测到(通常,编译器会抱怨如果在构造函数)。

无论如何,如果你有一个复杂的构造函数来调用其他成员函数而你没有进行单元测试,那么可能会调用虚拟pure。