C++中声明对象指针的疑问

1051747376 2015-01-18 10:43:33
初学C++, 在学习的时候写了如下代码:
注:win8.1+vs2013

#include "stdafx.h"
#include <iostream>
using namespace std;
class CAnimal
{
private:
int isAlive;
public:
CAnimal()
{
cout << "CAnimal constructor" << endl;
}
void breath()
{
cout << "I am CAnimal" << endl;
}
};
class CFish:public CAnimal
{
private:
int inWater;
public:
CFish()
{
cout << "CFish constructor" << endl;
}
void breath()
{
cout << "I am CFish" << endl;
}
};

int _tmain(int argc, _TCHAR* argv[])
{
CAnimal *op = NULL;
op->breath();
while (1);
return 0;
}

运行之后程序的输出结果为:
I am CAnimal

令我疑惑的是,在声明指针op时我将其初始化为了NULL,之后并没有对其进行赋值,但从结果来看,op指向了类CAnimal所在的内存空间,为什么会这样呢?

我是这样理解的:在声明指针op时已经表明了其指向的类型,即CAnimal,而类中的方法在内存中只有一份,比如,我定义了

CAnimal animal1, animal2;

animal1与animal2的数据成员是各自唯一的,但两个对象所拥有的方法其实在内存中是一样的。所以无论op是NULL 还是&animal1或者&animal2,只要声明了op所指向的类型,通过op访问类的breath()方法时,op总能正确的输出,但如果在op=NULL的情况下访问类中的数据成员,则会出现错误。不知道这样理解对不对,希望高手不吝解疑。
...全文
649 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
lm_whales 2015-01-22
  • 打赏
  • 举报
回复
出现问题表现在两个方面 1)逻辑上 ,NULL指针,没有指向一个对象, 这样读写操作,完全是瞎搞,纯属无中生有。 2)实现上,NULL指针,很多时候这个指针, 指向操作系统保护的区域,这个操作会抛出异常。
lm_whales 2015-01-22
  • 打赏
  • 举报
回复
NULL指针并无对应对的对象 所以 isAlive 这种成员变量,是这个指针所指对象没有的 任何修改 对象 *this,或者读取 *this 的内容的行为 比如输出 isAlive , isAlive++ ,a= isAlive, isAlive=a 这种设计给成员变量赋值,或者取成员变量的值得行为 ,都是读写对象的行为。 写对象就会修改对象内容,读就需要求 *this 的值,或者 *this 的某个成员的值 二对象 *this 是NULL指针指向的内容,这个对象根本就没有创建. 是个不存在的对象.逻辑上(理论上)就不可读写, 实际上也不可读写。 一个类的成员函数,是通过 CAnimal 的对象,引用,以及指针调用的 例如 CAnimal an,ref =an,*p=&an; an.breath(); //这种形式,this指针参数,对应 an的地址 &an ref.breath();//这种形式,this指针参数,对应ref的地址&ref,也就是 an的地址 &an; //引用的地址就是所引用对象的地址,二者是同一个对象 p->breath();// 这种形式,p 或者经过隐式转换后的,合适的指针,就是传给函数 breath的this指针 成员函数内部 直接写成员函数名字调用函数,也是相当于 this ->breath() 直接把 该函数,自己的隐藏参数this 指针,传递给所调用的函数 这样, NULL ->breath() 调用函数,代码进入函数 breath()内部, this指针,就是NULL了 这样,*this 本来应该有个对象对应的,现在没了 一旦读写 *this,,,,通常就是读写他的成员变量,就会出问题。 C,C++函数参数,的定义(初始化数据), 是参数传递过程,完成的, 返回地址保护,和参数传递完成后, 代码跳入函数执行,此时参数已经完成传递了。
1051747376 2015-01-22
  • 打赏
  • 举报
回复
引用 14 楼 lm_whales 的回复:
......................
int _tmain(int argc, _TCHAR* argv[])
{
    CAnimal *op = NULL;
    op->breath(); // NULL 指针解引用,会出现不确定行为,
                             //这里由于breath没有改动 *this 的任何内容,也没读*this ;
                             //所以这个函数可以正常工作。
    while (1);
    return 0;
}
你把CAnimal 写成这样,看看还正常吗?
class CAnimal
{
private:
    int isAlive;
public:
    CAnimal()
    {
        cout << "CAnimal constructor" << endl;
    }
    void breath()
    {
        cout << "I am CAnimal"<<  isAlive<< endl;
    }
};
我这样子试过了,结果确实有问题,不过我不太明白你所说的“/这里由于breath没有改动 *this 的任何内容,也没读*this ;”这句话是什么意思,能解释下吗?
蒋晟 2015-01-22
  • 打赏
  • 举报
回复
引用 10 楼 shenzhimingdashen 的回复:
[quote=引用 1 楼 jiangsheng 的回复:] 解引空指针是标准规定的不定义行为。编译器的任何行为都是符合标准的,包括抛出异常、跳过你的函数、格式化你的硬盘等等。如果你要写跨平台的代码,你应该在调用之前就检查指针是否为nullptr。 Visual C++的编译器允许解引空指针,因为MFC的代码依赖于这个特性。
1楼说的不对。这个地方,就是C++跟java之流的区别所在。C++最求性能,此处发现breath函数中未引用被对象任何成员变量,便直接调用函数了,根本没有解引用。不会发生什么未定义行为。[/quote] 标准都规定了这是未定义行为还在扯什么不会发生什么未定义行为。 C++ 标准§9.3.1/1 If a nonstatic member function of a class X is called for an object that is not of type X, or of a type derived from X, the behavior is undefined. §1.9/4: Certain other operations are described in this International Standard as undefined (for example, the effect of dereferencing the null pointer) 如果C++委员会规范化指针p为空时p->func()的行为,那么每个编译器的实现在编译->操作符的时候都要产生代码判断一次左边的指针是否为空,这样会造成大量性能损失,和C++的目标相悖。
Sandrer 2015-01-21
  • 打赏
  • 举报
回复
楼主试试这样: ((CAnimal *)NULL)->breath(); 再看看1楼说的东西
paschen 版主 2015-01-21
  • 打赏
  • 举报
回复
引用 1 楼 jiangsheng 的回复:
解引空指针是标准规定的不定义行为。编译器的任何行为都是符合标准的,包括抛出异常、跳过你的函数、格式化你的硬盘等等。如果你要写跨平台的代码,你应该在调用之前就检查指针是否为nullptr。 Visual C++的编译器允许解引空指针,因为MFC的代码依赖于这个特性。
引用 2 楼 fly_dragon_fly 的回复:
1. 类不占用内存,也不存在什么内存空间,只有在声明对象才开始有内存空间,声明指针依然不会有什么内存空间, 2. 函数不占用类对象的内存,即使不存在类对象,依然可以调用,只是不能访问成员变量。 3. this 是个指针,这儿应该不存在解引用
学习了!
lm_whales 2015-01-21
  • 打赏
  • 举报
回复
......................
int _tmain(int argc, _TCHAR* argv[])
{
    CAnimal *op = NULL;
    op->breath(); // NULL 指针解引用,会出现不确定行为,
                             //这里由于breath没有改动 *this 的任何内容,也没读*this ;
                             //所以这个函数可以正常工作。
    while (1);
    return 0;
}
你把CAnimal 写成这样,看看还正常吗?
class CAnimal
{
private:
    int isAlive;
public:
    CAnimal()
    {
        cout << "CAnimal constructor" << endl;
    }
    void breath()
    {
        cout << "I am CAnimal"<<  isAlive<< endl;
    }
};
renwotao2009 2015-01-20
  • 打赏
  • 举报
回复
哈哈,你可以在函数中用用this指针,到时候你就乐了
码工许师傅 2015-01-20
  • 打赏
  • 举报
回复
op->breath(); 相当于: CAnimal_breath(op); 在C++里叫名称修饰,当然具体修成的不一定是这样的(有具体编译器决定)。 就好像有如下代码:
struct Animal {
	int isAlive;
};

void Animal_breath(struct Animal* thiz) {
	puts("I am Animal");
}
你调用 Animal_breath(NULL); 当然不会有问题。
xcfine 2015-01-20
  • 打赏
  • 举报
回复
引用 2 楼 fly_dragon_fly 的回复:
1. 类不占用内存,也不存在什么内存空间,只有在声明对象才开始有内存空间,声明指针依然不会有什么内存空间, 2. 函数不占用类对象的内存,即使不存在类对象,依然可以调用,只是不能访问成员变量。 3. this 是个指针,这儿应该不存在解引用
引用 2 楼 fly_dragon_fly 的回复:
1. 类不占用内存,也不存在什么内存空间,只有在声明对象才开始有内存空间,声明指针依然不会有什么内存空间, 2. 函数不占用类对象的内存,即使不存在类对象,依然可以调用,只是不能访问成员变量。 3. this 是个指针,这儿应该不存在解引用
对头!
Jim_sh 2015-01-20
  • 打赏
  • 举报
回复
引用 1 楼 jiangsheng 的回复:
解引空指针是标准规定的不定义行为。编译器的任何行为都是符合标准的,包括抛出异常、跳过你的函数、格式化你的硬盘等等。如果你要写跨平台的代码,你应该在调用之前就检查指针是否为nullptr。 Visual C++的编译器允许解引空指针,因为MFC的代码依赖于这个特性。
1楼说的不对。这个地方,就是C++跟java之流的区别所在。C++最求性能,此处发现breath函数中未引用被对象任何成员变量,便直接调用函数了,根本没有解引用。不会发生什么未定义行为。
fly_dragon_fly 2015-01-19
  • 打赏
  • 举报
回复
1. 类不占用内存,也不存在什么内存空间,只有在声明对象才开始有内存空间,声明指针依然不会有什么内存空间, 2. 函数不占用类对象的内存,即使不存在类对象,依然可以调用,只是不能访问成员变量。 3. this 是个指针,这儿应该不存在解引用
breaksoftware 2015-01-19
  • 打赏
  • 举报
回复
原因很简单,楼主要知道class的非静态成员函数的第一个参数是this指针,该指针指向了这个对象的数据区。而成员函数只是一个函数地址罢了。你这么调用,没有牵扯到this的任何数据,所以就相当于调用了一个裸露的函数。所以没有问题。
zagnyu 2015-01-19
  • 打赏
  • 举报
回复
引用 1 楼 jiangsheng 的回复:
解引空指针是标准规定的不定义行为。编译器的任何行为都是符合标准的,包括抛出异常、跳过你的函数、格式化你的硬盘等等。如果你要写跨平台的代码,你应该在调用之前就检查指针是否为nullptr。 Visual C++的编译器允许解引空指针,因为MFC的代码依赖于这个特性。
引用 2 楼 fly_dragon_fly 的回复:
1. 类不占用内存,也不存在什么内存空间,只有在声明对象才开始有内存空间,声明指针依然不会有什么内存空间, 2. 函数不占用类对象的内存,即使不存在类对象,依然可以调用,只是不能访问成员变量。 3. this 是个指针,这儿应该不存在解引用
this指针在什么时候赋值?如果是在构造对象时赋值,那么这种调用时传入的this指针就是未定义的
zagnyu 2015-01-19
  • 打赏
  • 举报
回复
我觉得1楼说的有道理,这是个未定义行为,在不同的编译器下会有不同的效果,编译器怎么处理都合理
zfcode 2015-01-19
  • 打赏
  • 举报
回复
声明变量和你成员函数所在的内存空间不是相同的概念。
  • 打赏
  • 举报
回复
嗯,你的想法是对的。
yangyunzhao 2015-01-19
  • 打赏
  • 举报
回复
强烈建议楼主看看对应的汇编代码,一目了然! 楼主可以试试两种代码

void breath()
    {
        cout << "I am CAnimal" << endl;
    }

void breath()
    {
        cout << "I am CAnimal" << isAlive << endl;
    }
蒋晟 2015-01-19
  • 打赏
  • 举报
回复
解引空指针是标准规定的不定义行为。编译器的任何行为都是符合标准的,包括抛出异常、跳过你的函数、格式化你的硬盘等等。如果你要写跨平台的代码,你应该在调用之前就检查指针是否为nullptr。 Visual C++的编译器允许解引空指针,因为MFC的代码依赖于这个特性。

64,654

社区成员

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

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