C++ 防止 std::vector 中的对象切片
c++ prevent object slicing in std::vector
我想在 std::vector
中存储具有相同基数 class 的多个 classes。经过一些研究,我很明显必须使用指针来防止对象切片。但是,当我创建向量并向其添加元素并 return 时,生成的向量没有正确的值。
举个例子,这是我的两个 classes:
class Base {
public:
int var0;
}
class Derived : public Base {
public:
int var1;
}
这里是一个简单的 print
函数。作为规则,Base
的所有实例都应具有 var0 == 23
,而 Derived
的所有实例应具有 var0 != 23
.
void print(Base& value) {
if (value.var0 == 23) {
std::cout << "Base: " << value.var0 << std::endl;
} else {
Derived d = (Derived&) value;
std::cout << "Derived: " << d.var0 << ", " d.var1 << std::endl;
}
}
首先,这个确实像我想要的那样工作:
int main() {
int num = 10;
std::vector<Base*> vec;
for (int i = 0; i < num; i++) {
if (i % 2 == 0) {
Base b;
b.var0 = 23;
vec.push_back(&b);
} else {
Derived d;
d.var0 = 17;
d.var1 = 42;
vec.push_back(&d);
}
}
// ....
for (int i = 0; i < num; i++) {
print(*vec.at(i));
}
}
这会打印:
Base: 23
Derived: 17,42
Base: 23
Derived: 17,42
Base: 23
Derived: 17,42
Base: 23
Derived: 17,42
Base: 23
Derived: 17,42
现在,我希望这个向量由一个函数 return 编辑,所以我创建了一个函数:
std::vector<Base*> createVector(int num) {
std::vector<Base*> vec;
for (int i = 0; i < num; i++) {
if (i % 2 == 0) {
Base b;
b.var0 = 23;
vec.push_back(&b);
} else {
Derived d;
d.var0 = 17;
d.var1 = 42;
vec.push_back(&d);
}
}
return vec;
}
int main() {
int num = 10;
std::vector<Base*> vec = createVector(num);
// ....
for (int i = 0; i < num; i++) {
print(*vec.at(i));
}
}
这会打印:
Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42
这不是我想要的。我希望它像其他功能一样打印。
有什么办法可以解决这个问题吗?有什么方法可以使整个派生-class 事情变得更好一点?
您的程序行为未定义:
Base b;
b.var0 = 23;
vec.push_back(&b);
正在推回指向超出范围的变量 (b
) 的指针。
为什么不使用 std::vector<std::unique_ptr<Base>>
呢?对象切片不是问题,vector 会为你管理内存。
这与对象切片无关,而与您在向量中存储指向局部变量的指针有关。一旦声明变量的范围结束,变量就会被破坏,留下杂散指针,取消引用这些杂散指针会导致未定义的行为。
你说工作正常的程序也有这个问题。看似工作正常是一种或未定义行为的可能性。
我想在 std::vector
中存储具有相同基数 class 的多个 classes。经过一些研究,我很明显必须使用指针来防止对象切片。但是,当我创建向量并向其添加元素并 return 时,生成的向量没有正确的值。
举个例子,这是我的两个 classes:
class Base {
public:
int var0;
}
class Derived : public Base {
public:
int var1;
}
这里是一个简单的 print
函数。作为规则,Base
的所有实例都应具有 var0 == 23
,而 Derived
的所有实例应具有 var0 != 23
.
void print(Base& value) {
if (value.var0 == 23) {
std::cout << "Base: " << value.var0 << std::endl;
} else {
Derived d = (Derived&) value;
std::cout << "Derived: " << d.var0 << ", " d.var1 << std::endl;
}
}
首先,这个确实像我想要的那样工作:
int main() {
int num = 10;
std::vector<Base*> vec;
for (int i = 0; i < num; i++) {
if (i % 2 == 0) {
Base b;
b.var0 = 23;
vec.push_back(&b);
} else {
Derived d;
d.var0 = 17;
d.var1 = 42;
vec.push_back(&d);
}
}
// ....
for (int i = 0; i < num; i++) {
print(*vec.at(i));
}
}
这会打印:
Base: 23
Derived: 17,42
Base: 23
Derived: 17,42
Base: 23
Derived: 17,42
Base: 23
Derived: 17,42
Base: 23
Derived: 17,42
现在,我希望这个向量由一个函数 return 编辑,所以我创建了一个函数:
std::vector<Base*> createVector(int num) {
std::vector<Base*> vec;
for (int i = 0; i < num; i++) {
if (i % 2 == 0) {
Base b;
b.var0 = 23;
vec.push_back(&b);
} else {
Derived d;
d.var0 = 17;
d.var1 = 42;
vec.push_back(&d);
}
}
return vec;
}
int main() {
int num = 10;
std::vector<Base*> vec = createVector(num);
// ....
for (int i = 0; i < num; i++) {
print(*vec.at(i));
}
}
这会打印:
Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42
Derived: 2293232,0
Derived: 17,42
这不是我想要的。我希望它像其他功能一样打印。
有什么办法可以解决这个问题吗?有什么方法可以使整个派生-class 事情变得更好一点?
您的程序行为未定义:
Base b;
b.var0 = 23;
vec.push_back(&b);
正在推回指向超出范围的变量 (b
) 的指针。
为什么不使用 std::vector<std::unique_ptr<Base>>
呢?对象切片不是问题,vector 会为你管理内存。
这与对象切片无关,而与您在向量中存储指向局部变量的指针有关。一旦声明变量的范围结束,变量就会被破坏,留下杂散指针,取消引用这些杂散指针会导致未定义的行为。
你说工作正常的程序也有这个问题。看似工作正常是一种或未定义行为的可能性。