我感兴趣是否安全, DOWNCAST (感谢Mike)在某些条件下派生类的基类实例。我认为样本是最简单的解释方式:
struct BaseA
{
void foo() const {}
double bar_member;
// no virtuals here
};
struct DerivedA : public BaseA
{
double bar(double z) {bar_member = z;return bar_member;}
// DerivedA does not add ANY member variables to BaseA.
// It also does not introduce ANY virtual functions.
};
struct BaseB
{
BaseA baseA;
};
// add extra functionality to B, to do this,
// i also need more functionality on baseA.
struct DerivedB : public BaseB
{
// is this "safe"? since BaseA and DerivedA
// should have the same memory layout?!?
DerivedA& getA() {return *static_cast<DerivedA*>(&baseA);}
double foo(double z) {return getA().bar(z);}
};
#include <iostream>
int main(int argc, char** argv)
{
DerivedB b;
// compiles and prints expected result
std::cout << b.foo(argc) << std::endl;
}
在我的例子中,BaseA和BaseB类实现了某种视图概念。但是,它们还包含在派生类中添加更多功能所需的所有数据成员。我知道我可以实现视图只保留对提供功能的类的引用。但是,这会带来一些缺点:
我测试了我的代码,但是,我确实不相信这种方法。是的,我知道我可以用虚拟机等实现其中的一些功能但是非常性能至关重要......
欢迎任何想法,提示
马丁
感兴趣的人: 我通过以下方式改变了我的设计:
struct DerivedB : public BaseB
{
// encapsule the required extended functionality of BaseA member
struct OperateOnBaseA
{
OperateOnBaseA(BaseA& a);
double dosomething(double);
};
OperateOnBaseA a_extension;
DerivedB() :a_extension(baseA) {}
double foo(double z) {return a_extension.dosomething();}
};
答案 0 :(得分:1)
关于技术方面:当然禁止2011标准,5.2.9.11,静态演员。设B为D的基础:
如果类型“指向cv1 B的指针”的prvalue指向实际上是D类型对象的子对象的B,则结果指针指向类型D的封闭对象。否则,强制转换的结果是未定义。
另一方面,如果有人能找到一个不仅仅是这样做的实现,我会感到惊讶,因为类,方法和静态强制转换的明显实现。
答案 1 :(得分:0)
我发现你的行为不够干净。假设有一个“数据源类型”SourceA
和一个“数据视图类型”ViewB
,我会更像这样:
#include <iostream>
using namespace std;
template<typename T>
class SourceA_base
{
protected:
T data;
public:
using value_type = T;
SourceA_base(T&& a) : data(std::move(a)) { }
SourceA_base(T const& a) : data() { }
void foo() const {}
};
template<typename T>
class SourceA : public SourceA_base<T>
{
using B = SourceA_base<T>;
public:
using B::B;
T bar(T z) { return B::data = z; }
};
template<typename U>
class ViewB_base
{
protected:
U&& source;
public:
using value_type = typename std::remove_reference<U>::type::value_type;
ViewB_base(U&& a) : source(std::forward<U>(a)) { }
};
template<typename U>
class ViewB : public ViewB_base<U>
{
using B = ViewB_base<U>;
using T = typename B::value_type;
public:
using B::B;
T foo(T z) { return B::source.bar(z); }
};
int main ()
{
using S = SourceA<double>;
S a{3.14};
ViewB<S&> b{a};
std::cout << b.foo(6.28) << std::endl; // compiles and prints expected result
std::cout << ViewB<S>{S{2}}.foo(4) << std::endl; // still works
}
也就是说,所有(源/视图)类型都是模板化的,视图包含引用,并且没有向下转换。关于使用参考文献的预订:
我使用了rvalue-references,因此整个事情也适用于临时工,如我的第二个例子所示。也许构造函数在这里并不总是完整/正确。例如const
引用;我的实际会有完全模板化的构造函数(接受通用引用),但为了使它与单参数隐式定义的复制/移动构造函数合作有点棘手(需要类型特征和enable_if
)而我只想高亮显示想法在这里。
您还可以考虑使用元组来保存数据,利用其空基优化。
关于你原来的问题,这个垂头丧气是我永远不会做的事情;对于技术方面,请参阅Peter Schneider的回答。
答案 2 :(得分:0)
您现有的代码具有未定义的行为,如其他答案中所述。如果您不介意一些真正可怕的代码,可以通过在baseA
销毁对象并在同一位置创建DerivedA
来避免这种情况,因此向下转换是有效的:
#include <new>
struct DerivedB : public BaseB
{
DerivedB()
{
static_assert( sizeof(BaseA) == sizeof(DerivedA), "same size" );
baseA.~BaseA();
::new(&baseA) DerivedA();
}
~DerivedB()
{
getA().~DerivedA();
::new(&baseA) BaseA();
}
DerivedA& getA() {return *static_cast<DerivedA*>(&baseA);}
double foo(double z) {return getA().bar(z);}
};
析构函数恢复原始类型的对象,这样当BaseB
析构函数销毁其baseA
成员时,它会在对象上运行正确类型的析构函数。
但我会避免这样做并重新设计你的课程以另一种方式解决它。