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;
;

recordoperator[] (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 类在哪里?添加多态行为应该可以让您避免沮丧。 我刚刚添加了记录类。除了包含指向datas 的指针之外,它什么也不做。 @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,而是在做upcastdata 类是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++ 通过多态和指针自定义异构容器的主要内容,如果未能解决你的问题,请参考以下文章

C++ 自定义智能指针

在 Cython 中包装自定义类型 C++ 指针

C++中的指针容器

用于组合容器和自定义逻辑的 C++ 技术/库?

C++ 介绍——自定义数据类型

C++ 强制转换自定义类的非指针实例