当你分配时
_ptr = std::make_unique<B>();
这是因为B 是A 的派生类,但是_ptr 仍然是基类的unique_ptr。声明后不能更改变量的类型。
那么你有什么选择?
因为您知道_ptr 存储了一个指向派生类B 的指针,所以您可以在取消引用后进行强制转换:
_ptr = std::make_unique<B>();
// derefence the pointer, and cast the reference to `B&`.
B& reference_to_sister = (B&)(*_ptr);
int w = reference_to_sister.foo;
如果您采用这种方法,您将不得不以某种方式跟踪_ptr 中的派生类,否则您将面临遇到错误的风险。
或者,如果你使用 C++17,你可以使用std::variant:
class C : public A {
void initialize(A& a) {
// Do stuff if it's the base class
}
void initialize(B& b) {
// Do different stuff if it's derived
int w = b.foo;
}
C() {
_ptr = std::make_unique<B>(); // This works
// This takes the pointer, and calls 'initialize'
auto initialize_func = [&](auto& ptr) { initialize(*ptr); };
// This will call 'initialize(A&)' if it contains A,
// and it'll call 'initialize(B&)' if it contains B
std::visit(initialize_func, _ptr);
}
std::variant<std::unique_ptr<A>, std::unique_ptr<B>> _ptr;
};
事实上,如果你使用std::variant,即使A 和B 是完全不相关的类,它也可以工作。
这是另一个简短的 variant 示例
#include <variant>
#include <string>
#include <iostream>
void print(std::string& s) {
std::cout << "String: " << s << '\n';
}
void print(int i) {
std::cout << "Int: " << i << '\n';
}
void print_either(std::variant<std::string, int>& v) {
// This calls `print(std::string&) if v contained a string
// And it calls `print(int)` if v contained an int
std::visit([](auto& val) { print(val); }, v);
}
int main() {
// v is empty right now
std::variant<std::string, int> v;
// Put a string in v:
v = std::string("Hello, world");
print_either(v); //Prints "String: Hello, world"
// Put an int in v:
v = 13;
print_either(v); //Prints "Int: 13"
}