解决方案:该标准未定义跨不同翻译单元定义的免费对象(在命名空间范围内)的创建顺序。在尚未构造的对象上调用成员函数是未定义的行为。您可以改为在对象的翻译单元中定义以下函数,然后从其他函数调用它:
解决方案:虚拟基类是仅发生一次的基类,即使它被继承树中的不同类间接继承了不止一次。标准不允许这样做。使用dynamic_cast来做到这一点,并确保您的基类是多态的。
使函数接受T const **
好。
blockquote>
解决方案:您可能认为这比使用T **更安全,但实际上,它会使想要通过T**的人感到头痛:标准不允许这样做。 它给出了一个为什么禁止使用它的简洁示例:
好。
1 2 3 4 5 6 7
| int main() {
char const c = ’c’;
char* pc;
char const** pcc = &pc; //1: not allowed
*pcc = &c;
*pc = ’C’; //2: modifies a const object
} |
始终改为接受T const* const*;。
好。
关于C ++的另一个(封闭的)陷阱线程,因此正在寻找它们的人们会发现它们,是Stack Overflow问题C ++陷阱。
好。
好。
有些必须拥有C ++书籍,可帮助您避免常见的C ++陷阱:
有效的C ++
更有效的C ++
有效的STL
有效的STL书解释了bools问题的向量:)
Brian有一个很棒的清单:我要添加"总是将单参数构造函数标记为显式的(除非在极少数情况下需要自动转换)。
Scott Wheeler的网页C ++陷阱涵盖了一些主要的C ++陷阱。
并不是真正的技巧,而是一般准则:请检查您的资源。 C ++是一门古老的语言,多年来已经发生了很大的变化。最佳做法已随之改变,但不幸的是,那里仍然有很多旧信息。这里有一些很好的书籍推荐-我可以第二次购买每一本Scott Meyers C ++书籍。熟悉Boost和Boost中使用的编码样式-与该项目有关的人员处于C ++设计的最前沿。
不要重新发明轮子。熟悉STL和Boost,并尽可能使用它们的功能。特别是,除非有非常非常好的理由,否则请使用STL字符串和集合。很好地了解auto_ptr和Boost智能指针库,了解在哪种情况下打算使用每种类型的智能指针,然后在其他可能使用原始指针的地方使用智能指针。您的代码将同样高效,并且不易发生内存泄漏。
使用static_cast,dynamic_cast,const_cast和reinterpret_cast而不是C样式强制转换。与C样式的强制转换不同,它们将让您知道您是否真正要求的类型与您认为的不同。他们在视觉上脱颖而出,警告读者演员阵容正在发生。
我已经提到过几次了,但是Scott Meyers的书《 Effective C ++》和《 Effective STL》在帮助C ++方面确实物有所值。
想到这一点,史蒂文·德赫斯特(Steven Dewhurst)的C ++ Gotchas也是出色的"从战the"资源。他的有关滚动自己的异常以及如何构造异常的项目确实在一个项目中对我有所帮助。
与C一样使用C ++。在代码中具有创建和发布周期。
在C ++中,这不是异常安全的,因此可能无法执行发行版。在C ++中,我们使用RAII解决此问题。
所有具有手动创建和释放的资源都应包装在一个对象中,以便在构造函数/析构函数中完成这些操作。
1 2 3 4 5 6 7 8 9
| // C Code
void myFunc()
{
Plop* plop = createMyPlopResource();
// Use the plop
releaseMyPlopResource(plop);
} |
在C ++中,这应该包装在一个对象中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| // C++
class PlopResource
{
public:
PlopResource()
{
mPlop=createMyPlopResource();
// handle exceptions and errors.
}
~PlopResource()
{
releaseMyPlopResource(mPlop);
}
private:
Plop* mPlop;
};
void myFunc()
{
PlopResource plop;
// Use the plop
// Exception safe release on exit.
} |
我希望我没有学到很难的两个陷阱:
(1)默认情况下,很多输出(例如printf)被缓冲。如果您正在调试崩溃的代码,并且正在使用缓冲的调试语句,那么您看到的最后一个输出可能实际上并不是代码中遇到的最后一个打印语句。解决方案是在每次调试打印后刷新缓冲区(或完全关闭缓冲区)。
(2)注意初始化-(a)避免将类实例作为全局变量/静态变量; (b)尝试将所有成员变量初始化为ctor中的某个安全值,即使该值是微不足道的值(例如NULL)也是如此。
推理:不能保证全局对象初始化的顺序(全局变量包含静态变量),因此您最终可能会导致代码不确定性地失败,因为它取决于对象X在对象Y之前被初始化。如果您未明确初始化a基本类型的变量,例如类的成员bool或枚举,在令人惊讶的情况下,您将最终获得不同的值-再次,该行为似乎非常不
C ++ Gotchas这本书可能很有用。
这是我不幸碰到的几个陷阱。所有这些都有充分的理由,只有在被令我惊讶的行为咬伤之后,我才明白。
不阅读C ++ FAQ Lite。它解释了许多不好(也很好!)的做法。
不使用Boost。通过在可能的情况下利用Boost可以节省很多挫败感。
查看boost.org。它提供了许多附加功能,尤其是它们的智能指针实现。
对于初学者来说,最重要的陷阱是避免C和C ++之间的混淆。 C ++永远不应被视为带有类的更好的C或C,因为这会削减其功能,甚至使其变得危险(尤其是像在C中那样使用内存时)。
PRQA基于Scott Meyers,Bjarne Stroustrop和Herb Sutter的著作,有一个出色的免费C ++编码标准。它将所有这些信息汇总到一个文档中。
使用智能指针和容器类时要小心。
避免使用伪类和类...基本上是过度设计。
忘记定义虚拟的基类析构函数。这意味着在Base *上调用delete不会最终破坏派生部分。
保持名称空间平整(包括结构,类,名称空间和使用)。当程序无法编译时,这就是我的第一挫败感。
要弄糟,请经常使用直指针。而是将RAII用于几乎所有内容,当然要确保使用正确的智能指针。如果在句柄或指针类型类之外的任何地方编写"删除",则很可能会做错。
-
static_cast downcast on a virtual base class
并非如此……现在是关于我的误解:我认为以下内容中的A是虚拟基类,而实际上不是。根据10.3.1,它是一个多态类。在这里使用static_cast似乎很好。
1 2 3
| struct B { virtual ~B() {} };
struct D : B { }; |
总之,是的,这是一个危险的陷阱。
阅读《 C ++陷阱:避免编码和设计中的常见问题》一书。
忘记&,从而创建副本而不是引用。
这两次以不同的方式发生在我身上:
两者都很难发现,因为差异很小且很难看到,否则对象和引用在语法上的使用方式相同。
取消引用之前,请务必先检查指针。在C语言中,通常可以指望在取消引用错误指针时发生崩溃。在C ++中,您可以创建一个无效的引用,该引用将在远离问题根源的位置崩溃。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class SomeClass
{
...
void DoSomething()
{
++counter; // crash here!
}
int counter;
};
void Foo(SomeClass & ref)
{
...
ref.DoSomething(); // if DoSomething is virtual, you might crash here
...
}
void Bar(SomeClass * ptr)
{
Foo(*ptr); // if ptr is NULL, you have created an invalid reference
// which probably WILL NOT crash here
} |
意图是(x == 10):
1 2 3
| if (x = 10) {
//Do something
} |
我以为自己永远不会犯这个错误,但实际上我是最近才犯的。
文章/文章指针,参考和值非常有用。讨论避免避免陷阱和良好做法。您也可以浏览整个站点,其中包含主要针对C ++的编程技巧。
我花了很多年从事C ++开发。我写了几年前遇到的问题的简短摘要。符合标准的编译器不再是真正的问题,但我怀疑所概述的其他陷阱仍然有效。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <boost/shared_ptr.hpp>
class A {
public:
void nuke() {
boost::shared_ptr<A> (this);
}
};
int main(int argc, char** argv) {
A a;
a.nuke();
return(0);
} |