我想很长一段时间使用虚函数,类对象必须用指针分配,但我运行了一个测试示例,它似乎完全适用于堆栈上的对象,它只取决于语法如何你叫它。但是我所做的事情几乎就像是在创建一个让系统按我想要的方式做出的欺骗,那就是使用普通的基类指针来调用派生类的虚方法。
#include <iostream>
using namespace std;
class B{
public:
virtual void call() { cout<<"B\n"; };
};
class D: public B{
public:
void call() { cout<<"D\n"; }
};
int _tmain(int argc, _TCHAR* argv[])
{
B b;
D d;
b.call(); // output is "B"
d.call(); // output is "D"
B* obj = &d;
obj->call(); // output is "D"
return 0;
}
这在发布软件中是否可以接受,还是会引起人们的注意?我可以在我的软件中使用它,但我以前从未见过这样的事情。
在我的实际项目中,我有在堆栈上分配的基类和派生类,我们可以调用它们Base
,DerivedA
和DerivedB
。我想重组并向基础添加虚拟功能,我想我现在必须在堆上分配它们,但看起来我不必这样做。现在我的应用程序可能处于某种状态,所有操作都将在DerivedA
或DerivedB
上完成,所以我打算添加一个指针对象:
Base * obj = &DerivedA;
显然当我需要切换操作到其他对象时,我只做obj = &DerivedB
,基本上我将通过我的新obj
指针间接使用这两个对象进行所有操作。这听起来不错吗?
目前我的代码有if else
个语句可以在DerivedA
或DerivedB
上进行操作,这将消除这些。
答案 0 :(得分:8)
这完全正常。 C ++的一个重要原则是对象不应该关心谁拥有它们 - 包括堆栈。在同一个堆栈框架中保存一个指向派生对象的基指针并不常见,因为这会引发一个问题,为什么你不直接使用它,但它完全合法,以多态方式引用自动存储持续时间对象。
请注意,我说过#34;自动存储时间&#34;而不是&#34;在堆栈上#34;。这是因为相同的规则适用于任何自动存储持续时间对象类成员是最明显的例子。
答案 1 :(得分:4)
指向堆中对象的指针,指向堆栈上对象的指针或内存中任何其他位置的指针之间没有区别。
堆栈上的对象或堆上的对象之间没有(内部)区别:它们具有相同的成员,包括v表指针,无论如何。
因此,你所做的事情是完全可以接受的。当使用本地对象作为参数调用以指针作为参数的函数时,您可以看到这个习惯用法的更常见版本:
MyObj foo;
MyFunc(&foo);
&#34; C&#34;思考这个调用的方式,即给出的实际参数是 foo
的地址,在这里给出了一些见解:这是将指针传递给栈本地的标准方法反对一个函数。当然,在该函数体内,无法知道对象是在堆栈上,堆上还是其他地方;因此,无论如何,我们都希望虚拟函数的处理方式相同。
当然,有一个警告,即你不应该在堆栈上将对象声明为指针(例如MyObj* foo = &foo{};
)。请参阅here我的一个旧答案,其中我完全厌倦了这一点(虽然我后来编辑了这个问题,向新观众说明我的例子是不好的做法,即使它可以在某些情况下与某些编译器一起工作;看看编辑历史,看看答案的原始版本,这为我赢得了一些当之无愧的downvotes)。
编辑:您可以考虑使用引用,而不是直接使用指针。下面是一个完整的示例程序,用于演示:
#include <iostream>
using namespace std;
class Base
{
public:
virtual const char* func()
{
return "Base::foo\n";
}
};
class Derived: public Base
{
public:
virtual const char* func()
{
return "Derived::foo\n";
}
};
inline Base& pickObj (int i, Base& obj1, Base& obj2)
{
if (i > 0)
{
return obj1;
}
else
{
return obj2;
}
}
int main()
{
Base base;
Derived derived;
Base& my_obj{pickObj(0, base, derived)};
cout << my_obj.func();
}
运行时,输出为:
Derived::foo