目前,我正在处理一个令人愉快的遗留代码类,它通过switch-case实现多态:
class LegacyClass {
public:
enum InitType {TYPE_A, TYPE_B};
void init(InitType type) {m_type=type;}
int foo() {
if (m_type==TYPE_A)
{
/* ...A-specific work... */
return 1;
}
// else, TYPE_B:
/* ...B-specific work... */
return 2;
}
/** Lots more functions like this **/
private:
InitType m_type;
};
我想将其重构为适当的多态性,例如:
class RefactoredClass {
public:
virtual ~RefactoredClass(){}
virtual int foo()=0;
};
class Class_ImplA : public RefactoredClass {
public:
virtual ~Class_ImplA(){}
int foo() {
/* ...A-specific work... */
return 1;
}
};
class Class_ImplB : public RefactoredClass {
public:
virtual ~Class_ImplB(){}
int foo() {
/* ...B-specific work... */
return 2;
}
};
不幸的是,我有一个至关重要的问题:由于优化和架构方面的考虑,在LegacyClass
的主要用途中,我无法使用动态分配;实例是组合的不同类的成员:
class BigImportantClass{
/* ... */
private:
LegacyClass m_legacy;
}
(在此示例中,BigImportantClass
可以动态分配,但分配需要在一个连续的虚拟分段和一个new()
呼叫中;我无法进一步调用new()
ctor或后续初始化方法中的BigImportantClass
。)
是否有一种很好的方法可以多态地初始化具体实现,而不使用new()
?
到目前为止我自己的进展:我可以做的是提供char[]
缓冲区作为BigImportantClass
的成员,并以某种方式初始化{{1}的具体成员在那个记忆中。缓冲区足够大以容纳RefactoredClass
的所有实现。但是,我不知道如何安全地这样做。我知道放置新语法,但我对处理对齐很新(因此,the C++-FAQ...警告),并且RefactoredClass
接口的所有具体实现一般对齐听起来令人生畏。这是要走的路吗?或者我还有其他选择吗?
答案 0 :(得分:3)
这是一些代码......只是做了显而易见的事情。我不使用C++11's new union
features,这实际上可能是一种更有条理的方式来确保适当的对齐和大小并清理代码。
#include <iostream>
template <size_t A, size_t B>
struct max
{
static const size_t value = A > B ? A : B;
};
class X
{
public:
X(char x) { construct(x); }
X(const X& rhs)
{ rhs.interface().copy_construct_at_address(this); }
~X() { interface().~Interface(); }
X& operator=(const X& rhs)
{
// warning - not exception safe
interface().~Interface();
rhs.interface().copy_construct_at_address(this);
return *this;
}
struct Interface
{
virtual ~Interface() { }
virtual void f(int) = 0;
virtual void copy_construct_at_address(void*) const = 0;
};
Interface& interface()
{ return reinterpret_cast<Interface&>(data_); }
const Interface& interface() const
{ return reinterpret_cast<const Interface&>(data_); }
// for convenience use of virtual members...
void f(int x) { interface().f(x); }
private:
void construct(char x)
{
if (x == 'A') new (data_) Impl_A();
else if (x == 'B') new (data_) Impl_B();
}
struct Impl_A : Interface
{
Impl_A() : n_(10) { std::cout << "Impl_A(this " << this << ")\n"; }
~Impl_A() { std::cout << "~Impl_A(this " << this << ")\n"; }
void f(int x)
{ std::cout << "Impl_A::f(x " << x << ") n_ " << n_;
n_ += x / 3;
std::cout << " -> " << n_ << '\n'; }
void copy_construct_at_address(void* p) const { new (p) Impl_A(*this); }
int n_;
};
struct Impl_B : Interface
{
Impl_B() : n_(20) { std::cout << "Impl_B(this " << this << ")\n"; }
~Impl_B() { std::cout << "~Impl_B(this " << this << ")\n"; }
void f(int x)
{ std::cout << "Impl_B::f(x " << x << ") n_ " << n_;
n_ += x / 3.0;
std::cout << " -> " << n_ << '\n'; }
void copy_construct_at_address(void* p) const { new (p) Impl_B(*this); }
double n_;
};
union
{
double align_;
char data_[max<sizeof Impl_A, sizeof Impl_B>::value];
};
};
int main()
{
{
X a('A');
a.f(5);
X b('B');
b.f(5);
X x2(b);
x2.f(6);
x2 = a;
x2.f(7);
}
}
输出(带我的评论):
Impl_A(this 0018FF24)
Impl_A::f(x 5) n_ 10 -> 11
Impl_B(this 0018FF04)
Impl_B::f(x 5) n_ 20 -> 21.6667
Impl_B::f(x 6) n_ 21.6667 -> 23.6667
~Impl_B(this 0018FF14) // x2 = a morphs type
Impl_A::f(x 7) n_ 11 -> 13 // x2 value 11 copied per a's above
~Impl_A(this 0018FF14)
~Impl_B(this 0018FF04)
~Impl_A(this 0018FF24)
答案 1 :(得分:2)
我使用C ++ 11联合实现了这一点。此代码似乎在g ++ 4.8.2下工作,但它需要-std = gnu ++ 11或-std = c ++ 11标志。
#include <iostream>
class RefactoredClass {
public:
virtual ~RefactoredClass() { }; // Linking error if this is pure. Why?
virtual int foo() = 0;
};
class RefactorA : RefactoredClass {
double data1, data2, data3, data4;
public:
int foo() { return 1; }
~RefactorA() { std::cout << "Destroying RefactorA" << std::endl; }
};
class RefactorB : RefactoredClass {
int data;
public:
int foo () { return 2; }
~RefactorB() { std::cout << "Destroying RefactorB" << std::endl; }
};
// You may need to manually create copy, move, &ct operators for this.
// Requires C++11
union LegacyClass {
RefactorA refA;
RefactorB refB;
LegacyClass(char type) {
switch (type) {
case 'A':
new(this) RefactorA;
break;
case 'B':
new(this) RefactorB;
break;
default:
// Rut-row
break;
}
}
RefactoredClass * AsRefactoredClass() { return (RefactoredClass *)this; }
int foo() { return AsRefactoredClass()->foo(); }
~LegacyClass() { AsRefactoredClass()->~RefactoredClass(); }
};
int main (void) {
LegacyClass A('A');
LegacyClass B('B');
std::cout << A.foo() << std::endl;
std::cout << B.foo() << std::endl;
return 0;
}
答案 2 :(得分:0)
现在有人应该回答......所以这里是我的。
我建议使用char数组的联合和一个最大的整数类型:
union {
char refactored_class_buffer[ sizeof RefactoredClass ];
long long refactored_class_buffer_aligner;
};
我还强烈建议使用断言甚至是if(check)throw;进入你的工厂,这样你永远不会超过缓冲区的大小。
答案 3 :(得分:0)
如果每种情况的数据相同,并且您只是改变行为,则不需要在核心中进行分配 - 这基本上是使用单例策略的策略模式。您最终会在逻辑中使用多态,但不会在数据中使用。
class FooStrategy() {
virtual int foo(RefactoredClass& v)=0;
}
class RefactoredClass {
int foo() {
return this.fooStrategy(*this);
}
FooStrategy * fooStrategy;
};
class FooStrategyA : public FooStrategy {
//Use whichever singleton pattern you're happy with.
static FooStrategyA* instance() {
static FooStrategyA fooStrategy;
return &fooStrategy;
}
int foo(RefactoredClass& v) {
//Do something with v's data
}
}
//Same for FooStrategyB
然后,当您创建RefactoredClass
时,将其fooStrategy
设置为FooStrategyA::instance()
。