微妙破碎的C ++代码的邪恶的样本
我需要一些不好的C ++代码样本来说明违反良好做法的情况。 我想拿出我自己的例子,但是我很难提出一些没有人为的例子,而陷阱并不明显 (比看起来更难)。
例子会是这样的:
- 不使用
std::auto_ptr
成员为类定义复制构造函数,而是使用具有前向声明类的std::auto_ptr
成员。 - 从构造函数或析构函数(直接或间接)调用虚函数。
- 重载模板函数。
- 循环引用与
boost::shared_ptr
。 - 切片。
- 从Ccallback(直接或间接)抛出exception。
- 浮点比较平等。
- 具有原始指针成员的构造函数的exception安全性
- 从析构者投掷。
- 在不同的体系结构上编译时会发生整数溢出(
size_t
和int
不匹配)。 - 使容器迭代器无效。
…或任何其他可以想到的邪恶的东西。
我会欣赏一些指向现有资源,或一两个样本。
最令人头痛的parsing是C ++parsing这样的事情的一个令人惊讶的违反直觉的结果:
// 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 ++时,包括我自己在内)。
#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
不是exception安全的代码可能会以代码读者不明显的方式失败:
// 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; }
这个今天早些时候出现了 。 正如@Billy ONeal在那篇文章中指出的那样,在inputstream上循环,只检查eof()
,如果在stream上发生错误,会导致无限循环。 应该用good()
来代替。
坏:
while( !cin.eof() ) { getline(cin, input); }
好:
while( cin.good() ) { getline(cin, input); }
[信用:@詹姆斯McNellis]
优秀:
while (std::getline(std::cin, input)) { }
重载赋值运算符,但不能正确处理自赋值 。
你认为该计划将打印什么?
#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; bf(true); bf(1); A* a = &b; a->f(true); return 0; }
答: b
, b
, a
! 第一个打印输出是显而易见的。 第二个是b
因为B::f(bool)
的定义隐藏了A::f(int)
的定义。 第三个是因为重载parsing发生在静态types上。
(来源:本周大师,但我找不到这篇文章。)
参数依赖查找(ADL,也称为Koenig查找)不是大多数C ++程序员理解的,可能会导致一些非常不寻常的结果,尤其是当与模板结合使用时。
我讨论了ADL的一个主要缺陷,回答ADL的缺陷是什么?
重载分辨率涉及很多复杂性。 当在命名空间范围使用指令时,经常出现问题,特别是using namespace std
,因为该名称空间具有大量具有通用名称的实体。
下面是using namespace std
导致问题的两个更近的例子:
- 距离计算错误在c + +
- 为什么没有积极的条件显示在这个asbolute程序
这一个,恕我直言,也是棘手的:
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()
并且不会被编译器检测到(通常,编译器会抱怨如果在构造函数中调用纯虚函数) 。
无论如何,如果你有复杂的构造函数调用其他成员函数,并且没有适当的unit testing,可能会发生虚拟纯调用。