堆栈上的多态对象? 中,为什么C++中所有类的公共对象类都有问题。在这段引文中有这样一句话:

堆栈上的多态对象? 中,为什么C++中所有类的公共对象类都有问题。在这段引文中有这样一句话:,c++,polymorphism,C++,Polymorphism,使用通用基类意味着成本:对象必须被堆分配为多态的 我真的没有看它两次,因为它是我会认为很多眼睛扫描的句子,并报告任何错误陈述 然而,一位评论人士指出,情况可能并非如此,回想起来,我找不到任何好的理由证明这是真的。一个简短的测试用例产生VDerived::f()的预期结果 structvbase{ virtual void f(){std::cout我认为他是沿着不能将它存储在基类型变量中的路线走的。你说得对,如果它是派生类型,你可以将它存储在堆栈上,因为这没有什么特别的;从概念上讲,它只是存储类

使用通用基类意味着成本:对象必须被堆分配为多态的

我真的没有看它两次,因为它是我会认为很多眼睛扫描的句子,并报告任何错误陈述

然而,一位评论人士指出,情况可能并非如此,回想起来,我找不到任何好的理由证明这是真的。一个简短的测试用例产生
VDerived::f()的预期结果

structvbase{

virtual void f(){std::cout我认为他是沿着不能将它存储在基类型变量中的路线走的。你说得对,如果它是派生类型,你可以将它存储在堆栈上,因为这没有什么特别的;从概念上讲,它只是存储类的数据及其派生+vtable


编辑:好的,现在我很困惑,我们正在看这个例子。看起来你可能现在…

在我看来像多态性

<> P>多态在C++中有间接作用,即:<代码> Poto to T/<代码>或<代码>引用到T < /> >其中<代码> t>代码>完全无关。 Bjarne还错误地说“heap allocated”,这在技术上是不准确的


(注意:这并不意味着通用基类是“好的”!

读过它后,我认为重点是(特别是考虑到关于复制语义的第二句话),通用基类对于值处理的对象是无用的,因此它自然会导致通过引用进行更多处理,从而增加内存分配开销(想想模板向量和指针向量)

所以我认为他的意思是,对象必须与包含它们的任何结构分开分配,这将导致堆上更多的分配

PS(广告长颈鹿船长的评论):它确实没有功能

f(object o)
这意味着泛型函数必须是

f(object &o)
这意味着对象必须是多态的,这反过来意味着它必须单独分配,这通常意味着在堆上,尽管它可以在堆栈上。另一方面,现在您有:

template <typename T>
f(T o) // see, no reference
模板
f(to)//参见,无参考

对于大多数情况下,这是更有效的。这尤其是集合的情况,如果所有这些都是这样的基础对象(如java)的向量,那么就必须单独分配所有的对象。这将是很大的开销,尤其是在C++生成时的分配程序性能差的情况下。(java仍然有优势,因为复制垃圾收集器效率更高,C++不能使用其中一个)。 您可以这样编写测试函数

template<class T>
void test(T& obj)
{
    obj.f();
}
模板
空隙试验(T&obj)
{
obj.f();
}
不管这些类是否有虚函数,它仍然可以工作

比亚恩的说法是不正确的

对象,即类的实例,通过向其类声明中添加至少一个虚拟方法,可能会变得多态。虚拟方法添加一个间接级别,允许将调用重定向到调用方可能不知道的实际实现

为此,实例是堆分配还是堆栈分配并不重要,只要通过引用或指针(
T&instance
T*instance
)访问即可


此一般性断言滑到Bjarne的网页上的一个可能原因可能是,尽管如此,具有多态行为的堆分配实例仍然非常常见。这主要是因为通过某种工厂函数获得它的调用方确实不知道实际实现。

我想Bjarne的意思是在该代码中,
obj
,或者更准确地说,它所指向的对象不容易堆叠:

int f(int arg) 
{ 
    std::unique_ptr<Base> obj;    
    switch (arg) 
    { 
    case 1:  obj = std::make_unique<Derived1      >(); break; 
    case 2:  obj = std::make_unique<Derived2      >(); break; 
    default: obj = std::make_unique<DerivedDefault>(); break; 
    } 
    return obj->GetValue(); 
}
此代码包含一个对象切片错误:
obj
的堆栈空间仅与类
Base
的实例一样大;当
DerivedFactory
返回具有一些附加成员的派生类的对象时,它们将不会被复制到
obj
中,从而导致
obj
无效和不可用派生对象(甚至很可能无法用作基础对象。)

综上所述,有一类多态行为无法用堆栈对象以任何直接的方式实现


当然,任何完全构造的派生对象,无论存储在何处,都可以作为基本对象,因此可以多态地进行操作。这仅仅是继承类的对象与其基类之间的is-a关系的结果。

没有堆分配的多态性不仅是可能的,而且在某些情况下也是相关的和有用的所有生命案例。 这是一个很老的问题,已经有很多很好的答案。当然,大多数答案正确地表明,多态性可以在没有堆分配的情况下实现。一些答案试图解释在大多数相关用法中,多态性需要堆分配。 然而,似乎需要一个没有堆分配的多态性的可行用法的示例(即,不仅仅是单纯的语法示例表明它是可能的)


下面是一个简单的策略模式示例,使用多态性而不进行堆分配:

Strategy1
Strategy2
Strategy3
战略层次结构

class StrategyBase {
public:
    virtual ~StrategyBase() {}
    virtual void doSomething() const = 0;
};

class Strategy1 : public StrategyBase {
public:
    void doSomething() const override { std::cout << "Strategy1" << std::endl; }
};

class Strategy2 : public StrategyBase {
public:
    void doSomething() const override { std::cout << "Strategy2" << std::endl; }
};
class A {
    const StrategyBase* strategy;
public:
    // just for the example, could be implemented in other ways
    const static Strategy1 Strategy_1;
    const static Strategy2 Strategy_2;

    A(const StrategyBase& s): strategy(&s) {}
    void doSomething() const { strategy->doSomething(); }
};

const Strategy1 A::Strategy_1 {};
const Strategy2 A::Strategy_2 {};
用法示例

int main() {    
  // vector of non-polymorphic types, holding inner polymorphic strategy
  std::vector<A> vec { A::Strategy_1, A::Strategy_2 };

  // may also add strategy created on stack 
  // using unnamed struct just for the example
  struct : StrategyBase {
    void doSomething() const override {
      std::cout << "Strategy3" << std::endl;
    }
  } strategy3;

  vec.push_back(strategy3);

  for(auto a: vec) {
    a.doSomething();
  }
}

代码:

假设我们有两个类

class Base
{
public:
    int x = 1;
};

class Derived
    : public Base
{
public:
    int y = 5;
};

int main()
{
    Base o = Derived{ 50, 50 };

    std::cout << Derived{ o }.y;

    return 0;
}
类基
{
公众:
int x=1;
};
类派生
:公共基地
{
公众:
int y=5;
};
int main()
{
基o=导出的{50,50};
Strategy1
Strategy2
Strategy3
class Base
{
public:
    int x = 1;
};

class Derived
    : public Base
{
public:
    int y = 5;
};

int main()
{
    Base o = Derived{ 50, 50 };

    std::cout << Derived{ o }.y;

    return 0;
}