没有"新"的多态性

Cli*_*ton 7 c++ c++11

可以说我有类似以下内容:

class A { virtual void g() = 0 }

class B : public A { virtual void g() { ... } }
class C : public A { virtual void g() { ... } }

... f(bool x)
{
  if (x) { return B(); } else { return C(); }
}

bool get_boolean();

int main()
{
  bool b = get_boolean();
  ... x = f(b);
  x.g();
}
Run Code Online (Sandbox Code Playgroud)

无论如何,在没有调用的情况下执行类似上面的操作new,即仅在堆栈上?

Che*_*Alf 7

避免动态分配的一种简单方法是使用静态分配,这与动态分配大致相反.然而,必须小心地完成它,因为即使使用非线程程序,也可能无意中陷入这样的情况:代码的两个或更多部分都认为它们"拥有"某些静态分配的对象.更糟糕的是,这些本质上的全局变量(即使伪装成单身人士,或者在下面的代码中作为本地静态)本质上也可以作为意大利面通信的中心枢纽,其中混乱诱导信息在您无法想象的地方之间自由传播,完全不在你的控制.

所以,静态分配方案有一些缺点...... :-)

但是,让我们从那里开始:

// Using static allocation.

#include <iostream>
using namespace std;

struct A { virtual void g() = 0; };

struct B : A { virtual void g() override { wcout << "A\n"; } };
struct C : A { virtual void g() override { wcout << "B\n"; } };

A& f( bool const x )
{
    static B    theB;
    static C    theC;

    if( x ) { theB = B(); return theB; } else { theC = C(); return theC; }
}

bool get_boolean() { return false; }

int main()
{
    bool const b = get_boolean();
    A& x = f( b ); 
    x.g();
}
Run Code Online (Sandbox Code Playgroud)

为了避免静态分配方案的错误所有权缺陷,您可以使用C++ 自动分配在堆栈上提供存储(C++自动分配是按定义的堆栈,LIFO分配方案).但这意味着将存储传递给函数.然后,该函数可以返回对相关对象的引用:

// Using automatic storage (the stack)

#include <iostream>
using namespace std;

struct A { virtual void g() = 0; };

struct B : A { virtual void g() override { wcout << "A\n"; } };
struct C : A { virtual void g() override { wcout << "B\n"; } };

A& f( bool const x, B& b, C& c )
{
    if( x ) { b = B(); return b; } else { c = C(); return c; }
}

bool get_boolean() { return false; }

int main()
{
    bool const b = get_boolean();
    B   objBStorage;
    C   objCStorage;
    A&  x   = f( b, objBStorage, objCStorage ); 
    x.g();
}
Run Code Online (Sandbox Code Playgroud)

但即使我们选择忽略诸如带有副作用的构造等问题,即当我们轻率地假设类B并且C被设计为与这样的方案一起工作时,上述浪费存储.如果BC实例很大,那么可以考虑使用C++的工具在预先存在的存储中构造对象,称为placement new.由于内存对齐问题,在C++ 03中正确执行有点困难,但C++ 11提供了更好的支持,如下所示:

#include <iostream>
#include <memory>           // unique_ptr
#include <new>              // new
#include <type_traits>      // aligned_storage
using namespace std;

typedef unsigned char Byte;

struct A { virtual void g() = 0; };

struct B : A { virtual void g() override { wcout << "A\n"; } };
struct C : A { virtual void g() override { wcout << "B\n"; } };

A* f( bool const x, void* storage )
{
    return (x? static_cast<A*>( ::new( storage ) B() ) : ::new( storage ) C());
}

bool get_boolean() { return false; }

void destroyA( A* p ) { p->~A(); }

int main()
{
    enum{ enoughBytes = 
        (sizeof( B ) > sizeof( C ))? sizeof( B ) : sizeof( C ) };
    typedef aligned_storage< enoughBytes >::type StorageForBOrC;

    bool const b = get_boolean();
    StorageForBOrC storage;
    A* const pX = f( b, &storage );
    unique_ptr<A, void(*)(A*)> const cleanup( pX, destroyA );
    pX->g();
}
Run Code Online (Sandbox Code Playgroud)

现在,我会选择上述哪一项?

我会选择严格限制但简单的即时静态分配,还是选择内存浪费自动分配,或者......优化但有些复杂的就地对象构造?

答案是,我不会选择任何一个!

我不会专注于微观效率,而是专注于清晰度正确性,因此只需要动态分配的性能.为了正确,我会使用智能指针来表示功能结果.如果这真的让事情变得迟钝,我或许会考虑使用专用的小对象分配器.

总之,不要担心小东西!:-)


Jur*_*aho 3

在函数f对象中B()C()都是临时的,因此只能f按值返回它们。

也许boost::variant适合你。那么您甚至不需要将该方法设为虚拟方法或从公共基类派生。