没有初始化的类对象里有指针成员,为何给对象一个引用就不会出错?

彭家老三 2013-09-18 10:35:30
如下代码,把引用注释掉,运行的时候就会中断说:使用了未初始化的变量
但是加上引用后,就可以输出CCCCCCCC
另外,如果给类加个空的构造函数,不要引用也可以输出CCCCCCCC。
请问各位大神,这是什么情况?
引用起了什么作用?空的构造函数又起了什么作用?


#include <iostream>
using namespace std;

class A
{
public:
//A(){}
int * pI;
int i;
};

int main()
{
A a;
//A& b = a;

cout<<a.pI<<endl;
system("pause");
}

...全文
378 22 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
警告和具体编译器有关
max_min_ 2013-09-20
  • 打赏
  • 举报
回复
引用必须初始化的! 估计在引用的时候给对象同时初始化了, 对于重新定义了构造函数初始化了对象的, 默认的有问题,这得看默认构造函数怎么搞的了啊,
我看你有戏 2013-09-20
  • 打赏
  • 举报
回复
你这样用指针相当危险
lm_whales 2013-09-20
  • 打赏
  • 举报
回复
个人认为,引用后初始化,显然不如定义初始化(包含构造函数初始化)好。 在最大警告(错误等级下),编译时加警告, 似乎也未尝不可,因为警告,毕竟不是错误, 所以,如果引用后初始化,自然可以不必理会警告; 而有了警告,调试有问题以后,或者比较关注警告,可以在第一时间,找到指针没有初始化的错误。 除非标准另有规定。
fancexiao 2013-09-19
  • 打赏
  • 举报
回复
我用断点调试了下,,个人感觉是指针的问题,,
ri_aje 2013-09-19
  • 打赏
  • 举报
回复
又试了试,发现下面几种用法对应的汇编都不包含 __RTC_UninitUse 的调用,即运行时程序不会出那个为初始化变量的对话框。

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

int main()
{
    A a;
    A* b = &a;
 
    cout<<a.pI<<endl;
    system("pause");
}

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

void f (A&) {}

int main()
{
    A a;
    f(a);
 
    cout<<a.pI<<endl;
    system("pause");
}

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

void f (A*) {}

int main()
{
    A a;
    //f(&a);
 
    cout<<a.pI<<endl;
    system("pause");
}
总结一下,我觉得 vs2010 的逻辑是这样的。 (1) debug 模式下 /RTC 开关打开后,会执行必要的运行时检查,比如为初始化变量的使用。 (2) 具体的实现逻辑是,分配其他变量,用以记录被检测变量是否初始化,如果没有则调用 __RTC_UninitUse 中断程序。 (3) 但是判断某变量初始化是各很复杂的问题。如果只有一个变量 a 的时候,那么对于 a.pI 的初始化只能通过 a 完成,所以只需要检测对 a 的动作即可。但是当引入 a 的引用或指针时,同样的初始化可以通过这些间接的途径完成,因此检查工作就变得复杂了,而且随着这类间接途径的增加,检查的复杂度之迅速变得不可控制的。尤其是怎样检查和到底支持到什么样的复杂度很不容易通过程序量化,基本像个人工智能的复杂过程。所以我猜,vs2010 如果发现存在间接路径(引用或指针)能够影响变量的初始化,则取消对应的检查,因为反正也不可能检测所有路径,索性就干脆别检查了。结果就是加了个引用,貌似 a.pI 就初始化了似的。其实不然,只是 vs2010 太累了,不帮你进行运行时检查了。
modyaj 2013-09-19
  • 打赏
  • 举报
回复
系统默认时不会做* pI的工作 因为他很危险 如果你重载了默认的函数 但是没初始化 系统会认为 你注意到了 但是你确实不想初始化 ---个人理解 等大牛的解答吧
ri_aje 2013-09-19
  • 打赏
  • 举报
回复
用 vs2010 试了一下,debug 下可以看到楼主说的现象。 看了一下汇编,没用引用或默认构造函数的时候,有一个 __RTC_UninitUse 函数,导致了运行时那个对话框。 加了饮用以后,__RTC_UninitUse 调用就取消了。 难道 vs2010 debug 下的编译逻辑认为使用引用某种程度上相当于初始化了? 虽然主楼程序中注释掉的两句没有一个执行了正确的初始化任务。
ri_aje 2013-09-19
  • 打赏
  • 举报
回复
引用 13 楼 lm_whales 的回复:
[quote=引用 11 楼 ri_aje 的回复:] 又试了试,发现下面几种用法对应的汇编都不包含 __RTC_UninitUse 的调用,即运行时程序不会出那个为初始化变量的对话框。

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

int main()
{
    A a;
    A* b = &a;
 
    cout<<a.pI<<endl;
    system("pause");
}

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

void f (A&) {}

int main()
{
    A a;
    f(a);
 
    cout<<a.pI<<endl;
    system("pause");
}

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

void f (A*) {}

int main()
{
    A a;
    //f(&a);
 
    cout<<a.pI<<endl;
    system("pause");
}
总结一下,我觉得 vs2010 的逻辑是这样的。 (1) debug 模式下 /RTC 开关打开后,会执行必要的运行时检查,比如为初始化变量的使用。 (2) 具体的实现逻辑是,分配其他变量,用以记录被检测变量是否初始化,如果没有则调用 __RTC_UninitUse 中断程序。 (3) 但是判断某变量初始化是各很复杂的问题。如果只有一个变量 a 的时候,那么对于 a.pI 的初始化只能通过 a 完成,所以只需要检测对 a 的动作即可。但是当引入 a 的引用或指针时,同样的初始化可以通过这些间接的途径完成,因此检查工作就变得复杂了,而且随着这类间接途径的增加,检查的复杂度之迅速变得不可控制的。尤其是怎样检查和到底支持到什么样的复杂度很不容易通过程序量化,基本像个人工智能的复杂过程。所以我猜,vs2010 如果发现存在间接路径(引用或指针)能够影响变量的初始化,则取消对应的检查,因为反正也不可能检测所有路径,索性就干脆别检查了。结果就是加了个引用,貌似 a.pI 就初始化了似的。其实不然,只是 vs2010 太累了,不帮你进行运行时检查了。
++ 应该是这样,不过,在这种撂挑子的地方,是否可以加个警告呢? VS是否已经加了警告了(警告等级控制). [/quote] 那个弹出的对话框上面有两个按钮:break and continue。break 中断执行,continue 我没试,看名字应该是忽略这个未初始化的告警对话框,让程序继续执行。 这种可忽略可重视的能力,不就相当于编译期的警告吗,只不过是以一种运行期的方式体现出来。 当然,如果同样的错误能够在编译期抓住,并提供警告会更好,但正确检测变量未初始化是一个不容易的问题,否则我估计各种编译器也早就解决了。
ri_aje 2013-09-19
  • 打赏
  • 举报
回复
引用 14 楼 mougaidong 的回复:
[quote=引用 11 楼 ri_aje 的回复:] 又试了试,发现下面几种用法对应的汇编都不包含 __RTC_UninitUse 的调用,即运行时程序不会出那个为初始化变量的对话框。

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

int main()
{
    A a;
    A* b = &a;
 
    cout<<a.pI<<endl;
    system("pause");
}

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

void f (A&) {}

int main()
{
    A a;
    f(a);
 
    cout<<a.pI<<endl;
    system("pause");
}

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

void f (A*) {}

int main()
{
    A a;
    //f(&a);
 
    cout<<a.pI<<endl;
    system("pause");
}
总结一下,我觉得 vs2010 的逻辑是这样的。 (1) debug 模式下 /RTC 开关打开后,会执行必要的运行时检查,比如为初始化变量的使用。 (2) 具体的实现逻辑是,分配其他变量,用以记录被检测变量是否初始化,如果没有则调用 __RTC_UninitUse 中断程序。 (3) 但是判断某变量初始化是各很复杂的问题。如果只有一个变量 a 的时候,那么对于 a.pI 的初始化只能通过 a 完成,所以只需要检测对 a 的动作即可。但是当引入 a 的引用或指针时,同样的初始化可以通过这些间接的途径完成,因此检查工作就变得复杂了,而且随着这类间接途径的增加,检查的复杂度之迅速变得不可控制的。尤其是怎样检查和到底支持到什么样的复杂度很不容易通过程序量化,基本像个人工智能的复杂过程。所以我猜,vs2010 如果发现存在间接路径(引用或指针)能够影响变量的初始化,则取消对应的检查,因为反正也不可能检测所有路径,索性就干脆别检查了。结果就是加了个引用,貌似 a.pI 就初始化了似的。其实不然,只是 vs2010 太累了,不帮你进行运行时检查了。
如果是这样的话,VS就太自以为是了,即便是debug模式,也不能对没有进行解引用的指针做如此苛刻的检查。[/quote] (1) #11 只是我基于一些现象的猜测,看起来有道理,实际上也可能是错误的。 (2) VS 有理由对未初始化的指针变量做检查,即便程序并没有对该指针解引用。in particular, c++11 4.1/1 says: A glvalue (3.10) of a non-function, non-array type T can be converted to a prvalue. ... if the object is uninitialized, a program that necessitates this conversion has undefined behavior. 避免 UB 是好程序的基本目标之一,VS 作为编译器采取措施帮助程序员无可厚非。
turing-complete 2013-09-19
  • 打赏
  • 举报
回复
引用 11 楼 ri_aje 的回复:
又试了试,发现下面几种用法对应的汇编都不包含 __RTC_UninitUse 的调用,即运行时程序不会出那个为初始化变量的对话框。

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

int main()
{
    A a;
    A* b = &a;
 
    cout<<a.pI<<endl;
    system("pause");
}

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

void f (A&) {}

int main()
{
    A a;
    f(a);
 
    cout<<a.pI<<endl;
    system("pause");
}

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

void f (A*) {}

int main()
{
    A a;
    //f(&a);
 
    cout<<a.pI<<endl;
    system("pause");
}
总结一下,我觉得 vs2010 的逻辑是这样的。 (1) debug 模式下 /RTC 开关打开后,会执行必要的运行时检查,比如为初始化变量的使用。 (2) 具体的实现逻辑是,分配其他变量,用以记录被检测变量是否初始化,如果没有则调用 __RTC_UninitUse 中断程序。 (3) 但是判断某变量初始化是各很复杂的问题。如果只有一个变量 a 的时候,那么对于 a.pI 的初始化只能通过 a 完成,所以只需要检测对 a 的动作即可。但是当引入 a 的引用或指针时,同样的初始化可以通过这些间接的途径完成,因此检查工作就变得复杂了,而且随着这类间接途径的增加,检查的复杂度之迅速变得不可控制的。尤其是怎样检查和到底支持到什么样的复杂度很不容易通过程序量化,基本像个人工智能的复杂过程。所以我猜,vs2010 如果发现存在间接路径(引用或指针)能够影响变量的初始化,则取消对应的检查,因为反正也不可能检测所有路径,索性就干脆别检查了。结果就是加了个引用,貌似 a.pI 就初始化了似的。其实不然,只是 vs2010 太累了,不帮你进行运行时检查了。
如果是这样的话,VS就太自以为是了,即便是debug模式,也不能对没有进行解引用的指针做如此苛刻的检查。
lm_whales 2013-09-19
  • 打赏
  • 举报
回复
引用 11 楼 ri_aje 的回复:
又试了试,发现下面几种用法对应的汇编都不包含 __RTC_UninitUse 的调用,即运行时程序不会出那个为初始化变量的对话框。

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

int main()
{
    A a;
    A* b = &a;
 
    cout<<a.pI<<endl;
    system("pause");
}

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

void f (A&) {}

int main()
{
    A a;
    f(a);
 
    cout<<a.pI<<endl;
    system("pause");
}

#include <iostream>
using namespace std;
 
struct A
{
    int * pI;
    int i;
};

void f (A*) {}

int main()
{
    A a;
    //f(&a);
 
    cout<<a.pI<<endl;
    system("pause");
}
总结一下,我觉得 vs2010 的逻辑是这样的。 (1) debug 模式下 /RTC 开关打开后,会执行必要的运行时检查,比如为初始化变量的使用。 (2) 具体的实现逻辑是,分配其他变量,用以记录被检测变量是否初始化,如果没有则调用 __RTC_UninitUse 中断程序。 (3) 但是判断某变量初始化是各很复杂的问题。如果只有一个变量 a 的时候,那么对于 a.pI 的初始化只能通过 a 完成,所以只需要检测对 a 的动作即可。但是当引入 a 的引用或指针时,同样的初始化可以通过这些间接的途径完成,因此检查工作就变得复杂了,而且随着这类间接途径的增加,检查的复杂度之迅速变得不可控制的。尤其是怎样检查和到底支持到什么样的复杂度很不容易通过程序量化,基本像个人工智能的复杂过程。所以我猜,vs2010 如果发现存在间接路径(引用或指针)能够影响变量的初始化,则取消对应的检查,因为反正也不可能检测所有路径,索性就干脆别检查了。结果就是加了个引用,貌似 a.pI 就初始化了似的。其实不然,只是 vs2010 太累了,不帮你进行运行时检查了。
++ 应该是这样,不过,在这种撂挑子的地方,是否可以加个警告呢? VS是否已经加了警告了(警告等级控制).
SmallCoder1992 2013-09-19
  • 打赏
  • 举报
回复


SmallCoder1992 2013-09-19
  • 打赏
  • 举报
回复

vs2012是没有问题的,只是pi指针没有初始化的问题,所以输出为0000000,其实个人觉得你这么做没有意义,你得出的结果可能会随着编译器的不同出先不同结果,毕竟指针属于悬浮指针,它的值是不确定的
猫仔- 2013-09-18
  • 打赏
  • 举报
回复
发现。。真的额。。我用的也是2010。。发现了。感觉这里没初始化函数,那个引用居然代替了构造函数
猫仔- 2013-09-18
  • 打赏
  • 举报
回复
引用 6 楼 mougaidong 的回复:
如果你确定是运行时错误,而不是编译时错误的话。 我觉着你可以提bug了
那个构造函数就是初始化的,引用本来就是用来改变被引用的变量的
turing-complete 2013-09-18
  • 打赏
  • 举报
回复
如果你确定是运行时错误,而不是编译时错误的话。 我觉着你可以提bug了
yxc2013 2013-09-18
  • 打赏
  • 举报
回复
求大神指导!
彭家老三 2013-09-18
  • 打赏
  • 举报
回复
引用 2 楼 mougaidong 的回复:
你这个引用什么作用的没有起,用的什么编译器?
VS2010,多谢大神解惑。
彭家老三 2013-09-18
  • 打赏
  • 举报
回复
呃,ps一下,那个中断是在debug下才会有的。
加载更多回复(2)

65,187

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧