我写了一个看起来不错的手术,但是我不能确定它是否真的合法。
我有一个行类和扩展名
class Row {};
class SuperRow : public Row {};
我有一个返回向量的函数
std::vector<Row> GetRows(){
Row row1;
Row row2;
std::vector<Row> rows;
rows.push_back(row1);
rows.push_back(row2);
return rows;
}
我还有一个需要该数据的函数,但是它具有一个对象向量,这些对象扩展了主向量的数据类型。
void GetData(std::vector<SuperRow> *rows){
// in order to compile, must do this
*((std::vector<Row>*)rows) = GetRows();
}
这有效,但我正在查看时就好像做错了什么.. [编辑:好的,显然这很糟糕]
我也可能以不同的方式做同样的事情。
void GetRowsOtherWay(std::vector<Row> *rows){
*rows = GetRows();
}
void GetDataOtherWay(std::vector<SuperRow> *rows){
GetRowsOtherWay((std::vector<Row>*)rows);
}
这还不好吗?
答案 0 :(得分:2)
但是我不能确定它是否真的合法。
GetData
和GetDataOtherWay
不是“合法”。该程序的行为是不确定的。
GetRowsOtherWay
本身很好,但是您应该使用引用而不是指针参数。如果确实使用了指针,则可能应该检查它是否不为空。
对容器来说,这只是“不好”吗?因为在我们的代码库中的每个地方,我都看到人们将对象ptrs强制转换为其基类,即
GetThatRow((Row*)&mySuperRow);
将指针转换为指向base的指针是可以的。但是,派生对象的矢量不是从基础对象的矢量派生的。 Vector根本没有基类。
请注意,那里的演员阵容是多余且冒险的。转换为基本指针是隐式的,因此GetThatRow(&mySuperRow)
也可以正常工作。如果要使其明确,请使用static_cast
,以便编译器强制执行类型安全。
这有效
出现“正常工作”是一个可能的不确定行为的例子。
不应该将对象显式转换为基类的对象吗?
不一样。另外,我们在这里讨论指针的转换(尽管也适用于引用),而不是对象的转换。在相同的继承层次结构中,存在从指针类型到另一种指针类型的静态转换。从指向一个向量的指针到指向另一个向量的指针不存在静态转换。这些类型不是通过继承关联的。
我们怎么知道这是UB?
因为标准是这样(引自标准草案):
[basic.lval]
如果程序尝试通过以下方式访问对象的存储值 一种glvalue,其类型与以下一种不相似([conv.qual]) 以下类型行为未定义:
- 对象的动态类型,
- 一种类型,它是与对象的动态类型相对应的有符号或无符号类型,或者
- char,unsigned char或std :: byte类型。
std::vector<Row>
与*rows
的动态类型(即std::vector<SuperRow>
)相似,也与有符号或无符号的对应类型相似,也与{{1} },char
或unsigned char
。
一个好的经验法则:避免重新解释转换,如果需要,请确保您知道所涉及的语言规则,并且没有违反它们。永远不要使用C样式转换,因为无论您是否打算使用C样式转换,它都会重新解释转换。
如何从基础对象的向量创建派生对象的向量:首先,您需要一个从基础对象创建派生对象的函数。尽管可以通过将转换构造函数添加到派生类型来实现,但通常无法将基础对象转换为其派生类型。编写了此类函数后,请使用std::byte
将其应用于碱基向量。
将派生向量转换为基向量更简单,因为可以简单地复制并返回基子对象。此操作称为切片。