我在一两个小时前问了一个类似的问题,但这对我来说根本不同。在此之后,我应该很好。
class base
{
private:
string tame;
public:
void kaz(){}
virtual ~base() {}
void print() const
{
cout << tame << endl;
}
};
class derived: public base
{
private:
string taok;
public:
std::string name_;
explicit derived( const std::string& n ) : name_( n ) {}
derived(){}
void blah(){taok = "ok";}
void print() const
{
std::cout << "derived: " << name_ << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
base b;
derived d;
base * c = &b;
derived * e = (derived *)&b;
e->kaz();
system("pause");
return 0;
}
我知道这个例子中的向下转换不是好习惯,但我只是以它为例。因此,当我现在指向派生指针的基础对象时,我不明白为什么我仍然能够执行仅属于基类的某些操作。
例如,基类的接口有一个Kaz()方法,但派生方法没有。当我贬低时,为什么编译器不会因为Kaz()不是派生类接口的一部分而对我这样做呢?
为什么编译器在使用派生指针时不抱怨使用基类的成员?
为什么只有当我从一个方法中的基类接口访问一个成员而不是直接访问一个成员时,编译器才会对我大喊大叫?
例如:
我不能这样做:
e->print() //Program crashes
但我可以这样做:
e->tame = "Blah";
cout << e->tame << endl;
答案 0 :(得分:2)
首先,您应该使用dynamic_cast<derived*>(&b)
而不是C风格的演员。
如果要覆盖子类中的方法,则应将print()
方法声明为virtual
。
如果未在类方法中使用,则行e->tame = "Blah";
应该导致编译器发出错误,因为它被声明为private
。
最后可以在派生对象上调用kaz()
方法,因为子类包含基类中的所有方法以及派生类中定义的方法。
答案 1 :(得分:1)
派生类继承了基类的所有成员,因此kaz()
对象也存在derived
。如果您在kaz()
对象上调用derived
,则只会调用从base
继承的方法。如果从方法中访问继承的成员,或者直接无关紧要。
e
的问题在于它确实指向base
对象,而不是derived
。使用强制转换e = (derived *)&b
,你告诉编译器“我知道它看起来不像它,但这真的是derived *
,相信我!”。编译器相信你,因为你是主人。但是你撒了谎,&b
实际上不是一个derived*
。因此,当编译器试图在其上调用derived::print()
时会发生可怕的事情,在这种情况下会导致程序崩溃。
当您直接访问e->tame
时,也可能发生可怕的事情(编译器仍然将e
视为derived*
,而它只是base*
)。在这种情况下,无论如何,碰巧打印出预期值。
答案 2 :(得分:0)
从编译器的角度来看,e是一个“派生”的对象指针。
从运行时的角度来看,e指向“基础”对象,因此e-&gt; name_指向随机地址。尽管你投了它,它并没有改变它是一个被分配的“基础”对象的事实。
答案 3 :(得分:-1)
如果仔细观察一下,派生的构造函数都没有初始化基本名称,基本构造函数也没有(这是唯一使用的构造函数)。
因此,当您执行e->print()
时,您要打印的值未定义。
手动设置时,不再是未定义的。因此,对e->print()
的调用可以正常工作
你可以试试这个
在基类中定义一个接收名称
的非默认构造函数base(std::string name):tame(name){}