C++ 通过多态和指针自定义异构容器
Posted
技术标签:
【中文标题】C++ 通过多态和指针自定义异构容器【英文标题】:C++ custom heterogeneous container through polymorphism and pointers 【发布时间】:2012-03-29 15:08:10 【问题描述】:我正在围绕 sqlite3 编写一个非常简单且小型的包装器,并使用 sqlite3_get_table() 将结果作为char**
获取。我使用基础数据类能够将字段存储在统一容器中,即在此处记录,然后专门使用派生类型获取每种数据类型。
这是我的基类:
class data
private:
uint sz;
virtual void abstract() = 0;
public:
inline data(char* pd);
inline data();
uint size() return sz;
;
这是示例派生类:
class str : public data
private:
string* pdata;
virtual void abstract()
public:
inline str(char* pd);
inline operator string();
inline operator const char*();
;
和记录类:
class record
private:
ushort cols;
data** entries;
public:
record(char** ppdata, uint col_count);
inline data* operator [](ushort field);
inline uint num_fields() return cols;
;
record
的operator[]
(inline data* operator [](ushort field);
) 允许我以这种方式访问基类data
:
db::str* mys = dynamic_cast<db::str*>((*record)[3]);
在Arch Linux下编译:g++ -o main -lsqlite3 main.cpp
,编译没有问题。但是当我运行它时,我得到Segmentation Fault
。
注释掉 dynamic_cast 行让我很高兴,但我个人认为我在某种程度上做错了向下转换,而不是认为 data
类的定义或使用存在问题。
我也很欣赏对我的代码的任何激进冒犯,因为我知道我是一个新手。非常感谢。
【问题讨论】:
这个record
类在哪里?添加多态行为应该可以让您避免沮丧。
我刚刚添加了记录类。除了包含指向data
s 的指针之外,它什么也不做。
@sje397 是的,我的目标是添加多态行为,但我应该走错路了。我需要你的帮助! ;-)
【参考方案1】:
当您使用dynamic_cast
时,您需要检查结果是否为NULL
。当对象实际上不是您尝试转换为的类型的实例时,就会发生这种情况。
【讨论】:
我只是用db::data* pd = (*r)[3];
替换了dynamic_cast 行,我没有收到分段错误错误。 cout << pd;
在这种情况下给出0x21
。我该怎么办@Mark?
@ai64,你没有在问题中包含足够的信息来了解为什么dynamic_cast
不起作用。不过,该指针值看起来很可疑。我会看看你填写r
的地方。
我从来没有用过G++,但是你需要以某种方式启用RTTI吗? (发现 Microsoft VS 用户。:-))
我的目标很简单:将不同类型的数据、字符串、int、float、blob 和 none 存储在一个统一的数组中,称为记录。你会提出什么建议?我确信它在 CS 中是一个非常常见的问题,并且必须有我不知道的科学正确的 C++ 解决方案。在这里,我正在寻找答案。谢谢。
@ai64 在开发人员工作室中,除非为项目启用了运行时类型信息,否则您不能使用动态转换。我想在 G++ 中可能有一个命令行选项,你忽略了使用。如果您拥有的代码使用静态强制转换,那么很可能就是这种情况。如果您不检查动态强制转换的返回值是否与 null 相匹配,那么无论如何您都不必费心使用动态强制转换。并避免使用 c 风格的演员表!【参考方案2】:
您不是在做downcast
,而是在做upcast
。 data
类是operator[]
返回的基类,您将它转换为db::str*
,它是一个子类。并非每个data
都是str
,因此,对于初学者,您应该检查您要转换的内容是否是str
,以及转换是否不返回0。
另外,您应该习惯使用Valgrind,尤其是它的memcheck 工具。您可能会在几秒钟内确定位置和原因,作为 Arch 用户,我向您保证它在 repos 中。你用valgrind --tool=memcheck ./myprogram
运行你的程序。
【讨论】:
我知道我应该会遇到这样的问题,所以我禁用了转换为str
以外的任何类型,这样我就可以检查我的设计是否正确。是的,是str,但是我怎么知道结果不是0呢?如果您正在谈论的指针并非如此,正如我在对先前答案的评论中所解释的那样。这种情况我该怎么办?
@ai64 如果转换的结果不是 NULL,那么它是成功的并且你有一个 str
对象(嗯,实际上是指向一个对象)。问题可能不在你当时提到的那一行,而是在更远的地方,你使用你得到的str
值。正如我所指出的,你真的应该用 Valgrind 运行你拥有的任何东西,你会明白我的意思......
好的,我会检查并让大家知道。谢谢。
在浏览 valgrind 的手册页之前无法给出答案。【参考方案3】:
感谢 TC1 和 Mark Ransom。我试着摆弄我的问题,我终于意识到这是最好的解决方案,比较它的优点和它的权衡,就像我正在写的一个简单的包装器(sqlite3's sqlite3_get_table()
):
enum types INT, FLOAT, STRING, BLOB, NUL;
class bad_type ;
class data
private:
void* pdata;
types type;
public:
data(int dt)
int* tmp = new int(dt);
pdata = static_cast<void*>(tmp);
type = INT;
// constructors for other types to be added in a similar fashion.
operator int()
if (type == INT)
return *(static_cast<int*>(pdata));
throw bad_type();
// operators for other types to be added in a similar fashion.
;
通过这种方式,在数据类型有限的情况下,我使 data* 成为一种非常适合用于制作异构容器的指针类型(对不起,当我问这个问题时我不知道这个词;-)) , 在这种情况下为 5,因为它的工作水平很低。
我刚刚在我的测试中使用了它main
,如下所示:
data* array[3];
array[0] = new data(3);
cout << *(array[0]);
它确实给了我3
,当然它也可以将它应用于任何其他类型,尤其是在这种情况下,类型通常是基本类型而不是复杂的继承类型。
我开始相信在这种情况下static_cast
是可以容忍的,如果我错了,请纠正我。而且我真的很想知道您对这个解决方案的批评和赞赏,因为它可以帮助我作为初学者了解更多信息。
谢谢。
【讨论】:
我必须让你们都知道伙计们,我得出的结论是,尝试检测sqlite3_get_table()
返回结果的字段的大小和类型是最愚蠢的事情从结果本身来看,因为它的 char**
:-D ,但无论如何,对于任何初学者来说,都必须解决如何实现异构容器的良好而深入的理解。 ;-)以上是关于C++ 通过多态和指针自定义异构容器的主要内容,如果未能解决你的问题,请参考以下文章