c++ - C++ 多态性:是否允许模棱两可的成员类型?
问题描述
现在我需要一个类中不明确类型的成员。这里我说ambiguous,这意味着成员可能是同一基类的两种或多种类型。请参阅下面的代码。
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();
}
};
推荐阅读
- wordpress - 使用插件替换默认的 woocommerce 类别视图
- file-upload - 上传文件时的编码问题(HTML / Javascript)
- javascript - 如何用basename重命名许多文件名
- php - 如何使用 PHP 中的按钮请求删除数据
- python - 我不能使用“PermutationImportance”
- flutter - Flutter imagepicker.pickvideo 返回 jpg
- keras - tf.keras.callbacks.EarlyStopping 的参数值及其含义是什么
- python - 在 Dataframe 中查找共同值
- laravel - OR 和 AND 运算符在 laravel 中不起作用
- reactjs - 响应式导航栏切换按钮不起作用