【问题标题】:How to design properly a hierarchy of classes using pointers in C++如何使用 C++ 中的指针正确设计类的层次结构
【发布时间】:2011-02-24 03:51:19
【问题描述】:

我正在努力提高我对程序架构的了解,最近向我提出了一个问题related with this pointers issues I posted recently.

问题是在一个简单的层次结构中,你有一个 A 类,一个指向 B 类的指针,最后一个指向 C 类。不要将它与面向对象编程的继承属性混淆,但基本上我要说的是C 类是 B 类的孩子,B 类是 A 类的孩子。

关键是我希望能够通过指针直接从 A 类访问到 C 类(类比中的孙子)。其他一些成员指出这是糟糕的设计,主要是因为如果从 B 类集合中删除 C 类的实例,则会在 A 类集合中留下指向“无”的指针。那么,这是如何正确建模的呢?

非常感谢!

朱伦。

【问题讨论】:

标签: c++ architecture pointers


【解决方案1】:

考虑使用Composite Pattern,而不是让 A 类知道每个 C 类:

#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <stdexcept>

//------------------------------------------------------------------------------
class Component
{
public:
    typedef boost::ptr_vector<Component> Children;

    virtual void print() = 0;
    // Other operations

    virtual bool isLeaf() {return true;}

    virtual Children& children()
        {throw std::logic_error("Leaves can't have children");}
};

//------------------------------------------------------------------------------
class Composite : public Component
{
public:
    void print()
    {
        BOOST_FOREACH(Component& child, children_)
        {
            child.print();
        }
    }

    bool isLeaf() {return false;}

    Children& children() {return children_;}

private:
    Children children_;
};

//------------------------------------------------------------------------------
class Nut : public Component
{
public:
    Nut(std::string info) : info_(info) {}
    void print() {std::cout << info_ << std::endl;}

private:
    std::string info_;
};

//------------------------------------------------------------------------------
class Bolt : public Component
{
public:
    Bolt(std::string info) : info_(info) {}
    void print() {std::cout << info_ << std::endl;}

private:
    std::string info_;
};

//------------------------------------------------------------------------------
class Wheel : public Composite
{
public:
    Wheel(std::string info) : info_(info) {}
    void print()
    {
        std::cout << info_ << std::endl;
        Composite::print();
    }

private:
    std::string info_;
};

//------------------------------------------------------------------------------
class Vehicle : public Composite
{
public:
    Vehicle(std::string info) : info_(info) {}
    void print()
    {
        std::cout << info_ << std::endl;
        Composite::print();
        std::cout << "\n\n";
    }

private:
    std::string info_;
};

//------------------------------------------------------------------------------
int main()
{
    Wheel* wheel1 = new Wheel("Wheel1");
    wheel1->children().push_back(new Nut("Nut11"));
    wheel1->children().push_back(new Nut("Nut12"));
    wheel1->children().push_back(new Bolt("Bolt11"));
    wheel1->children().push_back(new Bolt("Bolt12"));

    Wheel* wheel2 = new Wheel("Wheel2");
    wheel2->children().push_back(new Nut("Nut21"));
    wheel2->children().push_back(new Nut("Nut22"));
    wheel2->children().push_back(new Bolt("Bolt21"));
    wheel2->children().push_back(new Bolt("Bolt22"));

    Vehicle bike("Bike");
    bike.children().push_back(wheel1);
    bike.children().push_back(wheel2);

    bike.print();
}

这个程序输出:

Bike
Wheel1
Nut11
Nut12
Bolt11
Bolt12
Wheel2
Nut21
Nut22
Bolt21
Bolt22

注意,当bike.print() 被调用时,print 会在所有子节点上递归调用。这就是您可以在祖父母不知道其所有孩子的情况下对所有孩子执行操作的方法。

Visitor Pattern 与 Composite 模式配合得非常好,所以我建议你也阅读一下那个。尤其是如果您有许多可以根据更基本的操作来实现的操作。

【讨论】:

    【解决方案2】:

    不幸的是,您只是在重新发明轮子。改变你的方法,所有 pointer-stuff 都可以自动管理。

    如果可能,请使用作曲。让class A 的对象持有class B 的对象,这将持有class C 的对象。这种明显而简单的方法可以为您节省大量时间,并使您的应用结构更好、更“干净”。

    如果您的类可以被视为相同类型的对象(例如,如果它们共享相同的接口),则从B 派生您的C,并从A 派生B。在那之后,您将不得不考虑它们的共享接口——就您的class A 中的virtual functions 而言,它就像一个基类。这实际上是一个棘手的时刻,但是一旦您习惯了以这种方式规划您的界面,您就想不出其他方式了。

    还要提一下,即使看起来像 “您当前的问题无法通过动态多态性建模” 或者您 “只是想不出合适的接口” em>,再试一次。 不要试图发明新的程序架构构建方法,而只是习惯现有的方法。

    【讨论】:

    • 您所指的称为复合模式:en.wikipedia.org/wiki/Composite_pattern
    • 谢谢科蒂。但是,如果我理解正确,我的类之间没有任何类型的继承。只是 B 是 A 的一部分,而 C 是 B 的一部分。同时 A 必须了解所有 C 部分。也许问题是我不应该使用指针来建模这种方法。
    【解决方案3】:

    我认为您所说的是 C 类派生自 B 类,B 类派生自 A 类。不要将类层次结构与指向类对象的指针混淆。你可以用一个对象(和一个指向对象的指针)来做你正在谈论的事情。如果您创建指向 C 类型对象的 C* 类型指针,则可以访问在类 B 和 A 中定义的所有公共和受保护方法。或者,如果您实现虚拟方法,则可以创建指向 A* 类型对象的指针类型 C。在运行时,vtable 将用于自动查找该虚拟方法的最低级别实现(即,如果在那里定义,则从 C 类,如果在那里定义,则从 B 类,否则从 A 类)。有大量用于学习 C++ 基础知识的好资源。如果你搜索的话,我敢肯定在 SO 上有这样一个列表的主题。

    编辑

    这是一个这样的链接:https://stackoverflow.com/questions/70159/what-is-the-best-source-to-learn-c

    【讨论】:

    • 抱歉,这个例子可能有误导性。不,根本没有继承。基本上是 A 类,假设 Car 和 B 类是轮子,而 C 类是这些轮子的螺丝部分。问题是我想让车直接知道所有这些螺丝。
    猜你喜欢
    • 2013-11-21
    • 2015-11-01
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    • 2014-12-15
    • 1970-01-01
    • 1970-01-01
    • 2014-12-06
    相关资源
    最近更新 更多