我在我的代码中遇到了这种奇怪的无指针情况,现在想知道是否有办法检测它(除了崩溃)。代码设法进入未构造对象的方法。当构造b1时,尚未构造a,并且b尝试使用它。在构造b2时,a被正确构造并且代码按预期工作。
除了显而易见的事情,"不要在你的代码中执行此操作,"我想知道是否有一种方法可以在编译或运行时检测到这一点。编译器根本没有检测到它,我只是在程序崩溃时初始化DLL时有一些关于运行托管代码的模糊和非常无益的消息。
我试图测试"这个",但它不是NULL因为内存已被设置 - 它只是没有调用构造函数所以内存是在不确定的状态。
我原以为编译器会坚持使用某些调试代码来检测何时发生,但我猜不会。
是否有任何断言或测试或编译时间开关我可以用来检测这种情况,或者它是否只是归结为,&#34;如果它受伤了,不要这样做?&#34; < / p>
OUTPUT:
(NULL)
test
#include "stdafx.h"
#include "cstring"
class Apple
{
char *sometimesinitialized;
public:
Apple () {
sometimesinitialized = new char[15];
strcpy_s(sometimesinitialized, 5, "test");
};
void test()
{
printf("%s\n", sometimesinitialized);
}
};
class Ball
{
public:
Ball();
};
Ball b1; // OOPS!
Apple a;
Ball b2; // Works as expected
Ball::Ball()
{
a.test();
}
int _tmain(int argc, _TCHAR* argv[])
{
scanf_s("%i");
return 0;
}
答案 0 :(得分:5)
如果B
的实例需要访问A
的实例,则应将其传递给B
的构造函数。
struct B {
B(A &a) {
a.test();
}
};
A a;
B b(a);
答案 1 :(得分:2)
保证具有静态存储的对象以完全标准保证的方式正确初始化的常见模式如下:
<强> global.hpp:强>
struct Foo;
Foo & globalFoo();
<强> global.cpp:强>
#include <foo.hpp>
Foo & globalFoo()
{
static Foo impl;
return impl;
}
任何需要访问全局Foo
对象的人只需调用globalFoo()
即可获取参考。该对象在首次使用之前进行初始化,并在程序结束时销毁,并且所有使用它的人都已被销毁。
答案 2 :(得分:0)
C ++程序的启动和关闭是两个领域,其中存在相当多的模糊性,你通常应该尽量做到最低限度。
标准规定,当您在模块中执行任何函数时,编译单元中的所有静态持续时间变量都将被初始化,但是您可以自己知道静态持续时间对象的状态在初始化或销毁期间是什么静态持续时间对象或循环依赖关系会发生什么。
此外,您不能依赖静态持续时间对象来执行模块的“服务注册”,因为标准规定编译单元中的静态持续时间变量的初始化不需要在{{1}之前执行但可能会延迟,直到您访问模块中的函数;因此,如果对模块的访问取决于注册模块,则可以简单地跳过初始化,使模块未注册且不可访问。添加这种后期初始化措辞只是为了能够支持语言中的动态库,但事实仍然是该标准并未强制要求这样的技术保证有效。
另一个重点还在于实施问题。例如在Windows中,我经历过关闭期间的错误可以被忽略(因此应用程序显然正在关闭而没有问题,但实际上它在关机期间因访问冲突而死)并且调试工具也没有在开始之前和main
完成之后正常工作,使这两个领域的调试问题变得非常困难。
例如,考虑使用日志记录工具:如果要在静态持续时间变量销毁期间调用日志记录并且日志记录本身依赖于静态持续时间变量(可能是在您调用日志记录时已经销毁了日志记录对象),则会遇到麻烦
也不保证构造和破坏发生的顺序(除了在编译单元中调用函数之前说明该单元的静态持续时间变量已被初始化的短语除外)。我经历过只是重建一个项目而不改变任何东西会产生不同的序列,可能是因为智能增量链接技术。)
我的建议是在静态初始化和销毁期间尽量减少所需,并避免做任何可能失败的事情。一旦你进入main,然后按照你想要的顺序显式地进行所需的初始化。以类似的方式,如果您对关闭时发生的事情以及在关闭时的顺序保持明确的控制,那么您将节省许多调试时间。
过去,我是懒惰和自动初始化技术的支持者,但IMO C ++并不是一种实用的方法,因为没有足够的保证。