代码之家  ›  专栏  ›  技术社区  ›  Alex B

细微破坏C++代码的邪恶样本

c++
  •  36
  • Alex B  · 技术社区  · 15 年前

    我需要一些坏的C++代码的样本,这将说明违反好的实践。我想举出我自己的例子,但我很难举出 不做作,陷阱不明显 (比看上去更难)。

    例如:

    1. 没有为具有 std::auto_ptr 成员,并使用 标准:自动 具有向前声明类的成员。
    2. 从构造函数或析构函数(直接或间接)调用虚拟函数。
    3. 重载模板函数。
    4. 循环引用 boost::shared_ptr .
    5. 切片。
    6. 从C回调抛出异常(直接或间接)。
    7. 相等的浮点比较。
    8. 具有原始指针成员的构造函数的异常安全性。
    9. 从析构函数中抛出。
    10. 在不同架构上编译时出现整数溢出(不匹配 size_t int ).
    11. 使容器迭代器无效。

    ……或者任何你能想到的邪恶的东西。

    我很欣赏一些现有资源的指针,或者一两个样本。

    8 回复  |  直到 15 年前
        1
  •  35
  •   In silico    15 年前

    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++时也包括了自己)。

        2
  •  16
  •   Palmik    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页

        3
  •  13
  •   In silico    15 年前

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

    // 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;
    }
    
        4
  •  8
  •   Community Mohan Dere    9 年前

    This one came up earlier tonight . 作为@ Billy ONeal 在那篇文章中指出,在输入流上循环,只检查 eof() ,如果流上发生错误,则可能导致无限循环。 good() 应该改为使用。

    坏的:

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

    好 啊:

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

    [信贷:@James McNellis]

    杰出的:

    while (std::getline(std::cin, input)) {
    }
    
        5
  •  7
  •   Dan    15 年前

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

        6
  •  7
  •   Neil G    15 年前

    你认为程序会打印什么?

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

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

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

        7
  •  5
  •   James McNellis    9 年前

    大多数C++程序员都不太理解依赖于参数的查找(ADL,也称为凯尼格查找),这可能会导致一些非常不寻常的结果,尤其是在与模板结合时。

    我讨论了ADL的一个主要陷阱 What are the pitfalls of ADL?


    过载解决方案涉及很多复杂度。在命名空间范围内使用指令时经常出现问题,特别是 using namespace std ,因为该命名空间有大量具有公共名称的实体。

    下面是最近的两个例子 使用命名空间std 引发问题:

        8
  •  2
  •   Simone    15 年前

    这个,伊姆霍,也很棘手:

    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() 未实施。显而易见的原因是派生在构造函数中尚未完成,因此您将最终调用virtual pure f() 编译器不会检测到(通常,如果在构造函数中调用纯虚拟,编译器会抱怨)。

    无论如何,如果您有调用其他成员函数的复杂构造函数,并且您没有进行单元测试,则可能会调用虚拟pure。