繁体   English   中英

以下程序如何在c ++中工作?

[英]How does the below program work in c++?

我刚刚创建了2个具有未定义行为的指针,并尝试调用没有创建对象的类成员函数?

我不明白吗

#include<iostream>

using namespace std;

class Animal
{
public:
  void talk()
  {
    cout<<"I am an animal"<<endl; 
  }
};

class Dog : public Animal
{  
public:
  void talk()
  {
    cout<<"bark"<<endl; 
  }
};

int main()
{
  Animal * a;
  Dog * d;

  d->talk();
  a->talk();  
} 

A)这是未定义的行为。 任何行为都可能发生。

B)因为您没有调用虚拟方法,所以很容易解释为什么未定义的行为实际上会这样做(而且我已经在我能找到的几乎所有编译器中进行了测试)。

在C ++中,调用成员方法等效于(实际上,如果没有定义)调用带有隐藏的“ this”变量的成员。 如果该方法是虚拟的,则必须通过vftable进行操作,但对于非虚拟方法则不需要。

所以

Foo::Bar(){}

大致相当于

Foo_Bar(Foo *this){}

并在调用代码中

Foo *foo = new Foo();
foo->bar();

第二行大致相当于

Foo_Bar(foo);

现在,这里进行了一些简化,正如我所说,其中一些可能是实现细节而不是规范。 但是,该行为成立(尽管依赖它是一个错误)。

但是,鉴于上述内容,请看一个实现:

void Foo::Bar(){printf("Hello, world!\n");}

和调用代码:

Foo *foo = 0;
foo->Bar();

正如我们所说,这大致等效于(因为我们不是虚拟的):

Foo *foo = 0;
Foo::Bar(foo);

这意味着我们正在调用以下方法的等效项:

void Foo_Bar(Foo* this)
{ printf("Hello, world\n"); }

现在,使用此方法,我们实际上并没有延迟this指针! 因此,很明显为什么在这种情况下该方法将起作用而不会失败。

实际的结果是,在空指针上调用非虚拟方法(该方法不会取消引用成员)通常会导致这种观察到的行为。 但是,依靠任何未定义的行为基本上是邪恶的。

当您执行某些行为未定义时, 任何事情都可能发生-包括它似乎起作用。 在这种情况下,这似乎就是您正在发生的事情。

您需要使用new运算符。

您需要更改:

void talk()

至:

virtual void talk()

如果您希望函数是多态的。 另外,您需要按以下方式实例化对象:

Animal* a = new Animal;
Dog* d = new Dog;

返回之前,请不要忘记释放它们:

delete a;
a = 0; 

delete d;
d = 0;

实际上,您将需要使用boost::shared_ptrstd::auto_ptr如下所示:

std::auto_ptr<Animal> a(new Animal);
std::auto_ptr<Dog> b(new Dog);

以上将节省您调用删除的需要。 另外,您可以使用自动存储:

Animal a;
Dog d;

通过以上操作,您将使用a.talk()d.talk()代替a->talk()d->talk()

我相信您的代码能够正常工作的原因是talk()方法实际上并未访问该类的任何成员变量。 换句话说,您实际上并没有访问隐式的this ,而这恰好是无效的。

我实际上曾经遇到过同样的问题。 我的代码正在调用null指针的成员函数,并且可以可靠地工作。 我最终在修改函数时发现了问题,以便实际上尝试访问成员变量。 更改之后,它确实崩溃了。

我不确定这是标准行为还是特定于编译器。 就我而言,我使用的是Microsoft Visual Studio 2008。

这是未定义的行为,因此可能会发生任何事情。

由于方法不访问被调用的对象的任何成员变量,因此它可能只打印正确的内容(不需要访问假定存在的对象的内存,因此不需要访问冲突必然发生)。

除非您的编译器在某处指定了这种行为(很可能不会),否则您当然不能指望这种情况会发生。

调用此非静态成员方法时,应构造类的对象,因此需要:

 Animal * a =  new Animal();
 Dog * d = new Animal();

 d->talk();
 a->talk(); 

 delete a;
 delete d;

要使用多态,您应该在talk()之前使用虚拟关键字

public:
virtual void talk()
  {
    cout<<"I am an animal"<<endl; 
  }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM