C 多态性:允许模棱两可的成员类型
C++ Polymorphism: Is ambiguous member type allowed?
现在我需要一类模棱两可的类型的成员。我在这里说模棱两可,这意味着该成员可以是同一基类的两种或多种类型。请参阅下面的代码。
class Base {
protected:
int m_int;
Base(int i) : m_int(i) {}
};
class Derived1 : Base {...};
class Derived2 : Base {...};
class AnotherClass {
private:
Base m_member; // <- this member
public:
AnotherClass(int selection) {
// if (selection)
// m_member = Derived1(...);
// else
// m_member = Derived2(...);
}
};
我不知道如何初始化此成员。有什么建议吗?也许指示/参考?
您应该使用std::variant
,指针或类型删除的包装器(编辑:我在最后添加了一个通用类型删除的包装器实现(。这是指针解决方案:
class AnotherClass {
private:
std::unique_ptr<Base> m_member;
public:
AnotherClass(int selection) {
if (selection)
m_member = std::make_unique<Derived1>(...);
else
m_member = std::make_unique<Derived2>(...);
}
};
不幸的是,AnotherClass
只能移动,而不是共配。如果您想能够复制它,则必须提供复制构造函数。复制构造函数必须能够克隆成员:
class Base {
protected:
int m_int;
Base(int i) : m_int(i) {}
public:
// For covariance, return a raw pointer instead.
virtual std::unique_ptr<Base> clone() const = 0;
};
class Derived1 : public Base {
public:
std::unique_ptr<Base> clone() const override {
return std::make_unique<Derived1>();
}
...
};
class Derived2 : public Base { .. same ... };
class AnotherClass {
public:
AnotherClass(const AnotherClass & other) : m_member(other.m_member->clone()) {}
AnotherClass(AnotherClass && other) = default;
.... same for assignment ...
编辑:
一种替代方法是使用一个基于类型的包装器,它照顾丑陋的东西:
#include <type_traits>
#include <memory>
template <class BaseType>
struct AbstractWrapper {
public:
virtual ~AbstractWrapper() = default;
virtual std::unique_ptr<AbstractWrapper<BaseType>> clone() = 0;
virtual BaseType * get() = 0;
};
template <class BaseType, class T>
struct ConcreteWrapper : public AbstractWrapper<BaseType> {
static_assert(std::is_base_of<BaseType, T>::value);
T data;
template <class U, class = std::enable_if_t<(!std::is_same_v<ConcreteWrapper, std::decay_t<U>>)>>
ConcreteWrapper(U && value) : data(std::forward<U>(value)) {}
std::unique_ptr<AbstractWrapper<BaseType>> clone() override
{
return std::make_unique<ConcreteWrapper>(*this);
}
BaseType * get() override { return &data; }
};
template <class BaseType>
class TypeErasedWrapper
{
public:
TypeErasedWrapper(const TypeErasedWrapper & other)
: container(other.container->clone())
{}
TypeErasedWrapper(TypeErasedWrapper && other) = default;
TypeErasedWrapper() {}
template <class U, class = std::enable_if_t<std::is_base_of_v<BaseType, std::decay_t<U>>>>
TypeErasedWrapper(U && concrete) {
container = std::make_unique<ConcreteWrapper<BaseType, std::decay_t<U>>>(std::forward<U>(concrete));
}
template <class U>
TypeErasedWrapper& operator = (U && concrete) {
*this = TypeErasedWrapper(std::forward<U>(concrete));
return *this;
}
TypeErasedWrapper& operator=(const TypeErasedWrapper & other) {
container = other.container->clone();
return *this;
}
TypeErasedWrapper& operator = (TypeErasedWrapper && other) = default;
BaseType * operator->() { return container->get();}
const BaseType * operator->() const { return container->get();}
BaseType * get() { return container ? container->get() : nullptr;}
const BaseType * get() const { return container? container->get() : nullptr;}
private:
std::unique_ptr<AbstractWrapper<BaseType>> container;
};
用法:
class AnotherClass {
private:
TypeErasedWrapper<Base> m_member;
public:
AnotherClass(int selection) {
if (selection)
m_member = Derived1(...);
else
m_member = Derived2(...);
}
int getValue() const {
// Assuming getBaseValue() is a method of Base
return m_member->getBaseValue();
}
};
static const Base& Base::getDerivedClass(int derivedID) {
if(derivedID == 1) return Derived1();
....
}
AnotherClass(int derivedID) : m_member(Base::getDerivedClass(derivedID))
初始化的最懒惰的方式是向构造函数添加一个参数。
private:
Base& m_member;
public:
AnotherClass(Base& ref) : m_member(ref) {}
请注意,这将是添加成员的一种非拥有方式。
如评论中所建议的,实现目标的正确方法是使用std::unique_ptr<Base>
。
class AnotherClass {
private:
std::unique_ptr<Base> m_ptr;
public:
AnotherClass (int selection) {
if (selection) m_ptr = std::make_unique<Derived1>();
else m_ptr = std::make_unique<Derived2>();
}
//...
}
您可以使用放置新,,但这是一个坏主意。
union DerivedAny {
char m_mem1[sizeof(Derived1)] alignas(Derived1);
char m_mem2[sizeof(Derived2)] alignas(Derived2);
};
class AnotherClass {
private:
char m_mem[sizeof(DerivedAny)] alignas(DerivedAny);
Base * base () { return reinterpret_cast<Base *>(m_mem); }
public:
AnotherClass (int selection) {
if (selection) new (m_mem) Derived1;
else new (m_mem) Derived2;
}
//...
};
由于构建了m_mem
,因此AnotherClass
需要一个破坏者才能正确清洁。
~AnotherClass () { base()->~Base(); }
然后您需要定义一个适当的复制构造函数和分配运算符。副本构造函数将特别棘手地执行,除非您可以恢复selection
的选择。
因此,这种方法充满了陷阱。
相关文章:
- 如何在c++中定义以struct为数据成员的类中的构造函数
- 使用成员在类中创建 lambda 表达式
- 使用静态成员声明类时遇到问题
- 没有公共构造函数作为另一个类模板成员的类模板
- 我们可以通过 IPC 传递具有动态管理成员的类对象吗?
- 具有 STL 向量类型成员的类的复制内存
- 内存中类位置的成员是否取决于类成员在类定义中的位置?
- C++成员变量类Q_PROPERTY QML 中不可用
- 提升 - 类没有名为"序列化"的成员(抽象类)?
- C++ - 移动具有固定大小的 c 样式数组成员的类的构造函数
- 具有未知结构作为成员的类
- 清除具有已删除赋值运算符的成员的类实例
- 错误:请求成员 .. 是非类类型"char"
- 没有成员的类的<运算符
- 从相同类型的静态成员进行类内初始化
- 如何设计一个始终是另一个类的成员的类
- 使用 Boost::Serialization 序列化具有 std::mt19937_64 成员的类
- C++ 将队列作为成员的类的构造函数和析构函数
- 移动具有常量成员的类的构造和分配
- 如果包含引用成员的类中缺少原始变量,为什么它仍然可以访问?