【问题标题】:Best way to store/return inherited classes in c++在 C++ 中存储/返回继承类的最佳方法
【发布时间】:2013-01-25 14:10:03
【问题描述】:

我正在为一些自动生成的类编写一个包装器,以提供更好的界面。我写了一个简化的例子来演示这个问题。

我有一个 A 类,它有一个指向 X 类对象的指针,然后我有一个 B 类(继承 A),它有一个指向 Y 类对象(继承 X)的指针。

这一切都很好,除了我不确定存储指向 X 和 Y 的指针的最佳方法是什么。我需要能够操作 A 类和 B 类对象的集合,就好像它们都是 A 一样(使用指向 X 的指针是我所需要的),这适用于我下面的示例。

我的实际问题是如何最好地处理 B 类的情况,它需要操作 Y 类对象而不是 X 类。我确信有一些非常明显的东西我可以在这里使用。每次我使用它时都必须重复转换这个变量并为每个继承我创建的 A 的类定义一个具有不同名称(例如 getY)的方法,这感觉很笨拙。可能有许多像 doStuff 这样的方法,所有这些方法都必须调用 getY。

class X {
private:
    int i;
public:
    X(int i) : i(i) {}
    int getI() {
        return i;
    }
};

class Y : public X {
private:
    int j;
public:
    Y(int i, int j) : X(i), j(j) {}
    int getJ() {
        return j;
    }
};

class A {
protected:
    X* x;
public:
    A(X* a) : x(a) {}
    X* get() {
        return x;
    }
};

class B : public A {
public:
    B(Y* y) : A(y) {}
private:
    //I could create a new function for each class like class B
    Y* getY() {
        return (Y*)x;
    }
public:
    void doStuff() {
        Y* y = getY();
        y->getJ();
        std::cout << "J: " << y->getJ() << cout::endl;
    }
};

从主目录中提取:

X* x = new X(5);
Y* y = new Y(5,6);
B b(y);
b.doStuff();

我想到的另一种选择是,当我构造 B 类时,我可以初始化一个 Y 类型变量的指针,该指针指向与 X 中设置的位置相同的位置。只要我的指针是 const,我认为应该是避免两个指向不同事物的引用。

欢迎提出任何想法。

谢谢。

【问题讨论】:

  • 在你走上继承之路之前。让我们问一下是 Y 还是 X 还是它们只是共享一个通用接口?
  • 是的,我简化的部分内容就是这个。对不起。 X 和 Y 类不是我定义的,而是相互继承的。我将 Y 视为 X 肯定会发生。
  • 好吧我明白X和Y现在不是你控制的了,A和B的情况下,我不会使用继承,而是使用接口。让 A 和 B 都实现接口。

标签: c++ pointers inheritance wrapper


【解决方案1】:

好吧,我要做的第一件事就是打破A和B之间的继承关系。你说它们有一个共同的接口,那么从定义这个接口开始:

class ILibraryWrapper {
public:
    virtual X * get() = 0;
};

我假设A和B共享很多代码(因此是继承关系),所以接下来定义一个模板类来实现接口并定义公共位:

template <class T> class LibraryWrapper : public ILibraryWrapper {
protected:
    T * pointer;

public:
    LibraryWrapper( T * value ) : pointer( value ) {}

    virtual X * get() override { return static_cast<X *>( this->pointer ); }
};

最后,像这样定义 A 和 B:

class A : public LibraryWrapper<X> {
public:
    A( X * value ) : LibraryWrapper( value ) {}
};

class B : public LibraryWrapper<Y> {
public:
    B( Y * value ) : LibraryWrapper( value ) {}

    void doStuff() {
        std::cout << "J: " << this->pointer->getJ() << cout::endl;
    }
};

实际上,您的所有代码都应该处理 ILibraryWrapper 指针或引用。如果某些代码绝对必须知道被包装对象的具体类型,那么您可以进行动态转换:

auto wrapper = dynamic_cast<LibraryWrapper<Y> *>( object );
if( wrapper != nullptr ) {
    // ...
}

但一般来说,最好在 ILibraryWrapper 接口中提供所有需要的功能。

【讨论】:

  • override 关键字是 c++11 的东西,对吧?我的编辑器和编译器真的不喜欢那样。我认为这只是为了强制执行安全吗?
  • 对。如果函数签名错误会产生编译器错误,但在运行时没有影响。
  • 另外,除非我遗漏了一些你需要定义构造函数的东西: B( Y * value ) : LibraryWrapper( value ) {} no?
  • 否... B 不继承自 LibraryWrapper
  • 其实这是一个错字,我的意思是 ,但我认为你的意思是它不应该有 。我一定特别愚蠢,就好像我尝试编译你的例子一样,我得到:“类'A'没有任何名为'LibraryWrapper'的字段”,错误:没有匹配函数调用'LibraryWrapper::LibraryWrapper ()',候选者是:LibraryWrapper::LibraryWrapper(T*) [with T = X]
【解决方案2】:

只要 Y 派生自 X,B 派生自 A,那么这很容易。

只要返回类型是多态的,虚拟函数就可以因返回类型而异。在 A 中实现以下内容:

virtual X* Get() const { return x; }

以及B中的以下内容:

virtual Y* Get() const { return static_cast< Y* >( A::Get() ); }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-07
    • 1970-01-01
    • 2020-03-29
    • 2017-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多