Base unique_ptr 的向量导致 emplace_back(new T()) 上的对象切片
Vector of Base unique_ptr causes object slicing on emplace_back(new T())
我试图将一个类型作为参数传递给一个方法,该方法将正确构造对象并将其推送到 unique_ptr 的向量,但是创建的对象始终是 Base 对象。使用 emplace_back() 时会发生这种情况,如果我只是实例化对象,同样可以正常工作。
在矢量外部构造对象工作正常,但是我不确定之后如何将指针移动到矢量。
body_parts.hpp
#include <vector>
#include <string>
#include <fmt/core.h>
using namespace std;
namespace uhcr {
class body_part
{
public:
string name = "Generic";
template <class T>
void add_body_part()
{
this->body_parts.emplace_back<T*>(new T());
fmt::print("{}\n", this->body_parts.back()->name);
}
private:
vector<unique_ptr<body_part>> body_parts;
};
class torso : public body_part
{
public:
string name = "Torso";
};
}
character.hpp
#include <string>
#include "components/body_parts.hpp"
using namespace std;
namespace uhcr {
class character : public body_part
{
public:
string name = "Character";
};
}
main.cpp
#define FMT_HEADER_ONLY
#include <memory>
#include <fmt/core.h>
#include "src/character.hpp"
using namespace fmt;
using namespace uhcr;
void create_human() {
character human;
human.add_body_part<torso>();
}
int main(void) {
create_human();
return 1;
}
错误在 add_body_part() 处,当 运行 此代码打印“通用”时。
您的代码中没有切片。你似乎期望虚拟成员变量,但没有这样的东西。使用虚拟方法时会得到预期的输出:
#include <memory>
#include <vector>
#include <iostream>
#include <string>
using namespace std;
class body_part
{
public:
virtual std::string getName() { return "Generic";}
template <class T>
void add_body_part()
{
this->body_parts.emplace_back<T*>(new T());
std::cout << this->body_parts.back()->getName();
}
virtual ~body_part() = default;
private:
vector<unique_ptr<body_part>> body_parts;
};
class torso : public body_part
{
public:
std::string getName() override { return "Torso"; }
};
class character : public body_part
{
public:
std::string getName() override { return "Character"; }
};
void create_human() {
character human;
human.add_body_part<torso>();
}
int main(void) {
create_human();
return 1;
}
这是具有相同效果的更简单的示例:
#include <iostream>
#include <string>
struct foo {
std::string name = "Generic";
void x(foo& f){
std::cout << f.name;
}
virtual ~foo() = default;
};
struct bar : foo {
std::string name = "Beneric";
};
int main () {
foo f;
bar b;
f.x(b);
}
bar
有两个 name
成员。在 foo
中,当您写 std::cout << f.name
时,它指的是 foo::name
而不是 bar::name
。如果 foo
实际上是 bar
,您也可以在 foo
中访问 bar::name
,但这会导致一些倒退的设计:
#include <iostream>
#include <string>
struct foo {
std::string name = "Generic";
void x(foo& f);
virtual ~foo() = default;
};
struct bar : foo {
std::string name = "Beneric";
};
void foo::x(foo& f) {
std::cout << dynamic_cast<bar&>(f).name;
}
int main () {
foo f;
bar b;
f.x(b);
}
在您的示例中,dynamic_cast 问题不大,因为您刚刚创建了一个 T
并且您知道它是一个 T
,但是您应该使用虚拟方法而不是依赖演员表。
您的子类中有多个名为 name
的数据成员。您可能想为 body_part
中的成员赋值,而不是声明隐藏它的新成员。
class body_part
{
public:
body_part() = default;
string name = "Generic";
template <class T>
void add_body_part()
{
this->body_parts.emplace_back<T*>(new T());
fmt::print("{}\n", this->body_parts.back()->name);
}
protected:
body_part(std::string name) : name(name) {}
private:
vector<unique_ptr<body_part>> body_parts;
};
class torso : public body_part
{
public:
torso() : body_part("Torso") {}
};
class character : public body_part
{
public:
character() : body_part("Character") {}
};
我试图将一个类型作为参数传递给一个方法,该方法将正确构造对象并将其推送到 unique_ptr 的向量,但是创建的对象始终是 Base 对象。使用 emplace_back() 时会发生这种情况,如果我只是实例化对象,同样可以正常工作。
在矢量外部构造对象工作正常,但是我不确定之后如何将指针移动到矢量。
body_parts.hpp
#include <vector>
#include <string>
#include <fmt/core.h>
using namespace std;
namespace uhcr {
class body_part
{
public:
string name = "Generic";
template <class T>
void add_body_part()
{
this->body_parts.emplace_back<T*>(new T());
fmt::print("{}\n", this->body_parts.back()->name);
}
private:
vector<unique_ptr<body_part>> body_parts;
};
class torso : public body_part
{
public:
string name = "Torso";
};
}
character.hpp
#include <string>
#include "components/body_parts.hpp"
using namespace std;
namespace uhcr {
class character : public body_part
{
public:
string name = "Character";
};
}
main.cpp
#define FMT_HEADER_ONLY
#include <memory>
#include <fmt/core.h>
#include "src/character.hpp"
using namespace fmt;
using namespace uhcr;
void create_human() {
character human;
human.add_body_part<torso>();
}
int main(void) {
create_human();
return 1;
}
错误在 add_body_part() 处,当 运行 此代码打印“通用”时。
您的代码中没有切片。你似乎期望虚拟成员变量,但没有这样的东西。使用虚拟方法时会得到预期的输出:
#include <memory>
#include <vector>
#include <iostream>
#include <string>
using namespace std;
class body_part
{
public:
virtual std::string getName() { return "Generic";}
template <class T>
void add_body_part()
{
this->body_parts.emplace_back<T*>(new T());
std::cout << this->body_parts.back()->getName();
}
virtual ~body_part() = default;
private:
vector<unique_ptr<body_part>> body_parts;
};
class torso : public body_part
{
public:
std::string getName() override { return "Torso"; }
};
class character : public body_part
{
public:
std::string getName() override { return "Character"; }
};
void create_human() {
character human;
human.add_body_part<torso>();
}
int main(void) {
create_human();
return 1;
}
这是具有相同效果的更简单的示例:
#include <iostream>
#include <string>
struct foo {
std::string name = "Generic";
void x(foo& f){
std::cout << f.name;
}
virtual ~foo() = default;
};
struct bar : foo {
std::string name = "Beneric";
};
int main () {
foo f;
bar b;
f.x(b);
}
bar
有两个 name
成员。在 foo
中,当您写 std::cout << f.name
时,它指的是 foo::name
而不是 bar::name
。如果 foo
实际上是 bar
,您也可以在 foo
中访问 bar::name
,但这会导致一些倒退的设计:
#include <iostream>
#include <string>
struct foo {
std::string name = "Generic";
void x(foo& f);
virtual ~foo() = default;
};
struct bar : foo {
std::string name = "Beneric";
};
void foo::x(foo& f) {
std::cout << dynamic_cast<bar&>(f).name;
}
int main () {
foo f;
bar b;
f.x(b);
}
在您的示例中,dynamic_cast 问题不大,因为您刚刚创建了一个 T
并且您知道它是一个 T
,但是您应该使用虚拟方法而不是依赖演员表。
您的子类中有多个名为 name
的数据成员。您可能想为 body_part
中的成员赋值,而不是声明隐藏它的新成员。
class body_part
{
public:
body_part() = default;
string name = "Generic";
template <class T>
void add_body_part()
{
this->body_parts.emplace_back<T*>(new T());
fmt::print("{}\n", this->body_parts.back()->name);
}
protected:
body_part(std::string name) : name(name) {}
private:
vector<unique_ptr<body_part>> body_parts;
};
class torso : public body_part
{
public:
torso() : body_part("Torso") {}
};
class character : public body_part
{
public:
character() : body_part("Character") {}
};