【问题标题】:Such a thing as C++ design pattern to avoid pointers?诸如 C++ 设计模式之类的东西可以避免指针?
【发布时间】:2011-02-05 03:43:50
【问题描述】:

我有一个类层次结构,如下例所示,其中State 包含ZipCodes 列表和Citys 列表,每个列表都包含指向ZipCodes 的指针。

目标是能够更新ZipCodes 而无需更新Citys(或创建City 的新实例)。

下面的 C++ 代码满足此要求,但它使用指针,由于thisthat,我更愿意避免使用指针。 如何重新设计这个 [naive] 实现,使其不依赖于指针?感谢您的帮助!

编辑:更新下面的代码以使用 boost::shared_ptr 而不是原始指针。请注意,StateCityZipCode 只是示例名称,它们被证明是糟糕的选择名称(我可以选择“A”、“B”和“C”),因为实际代码允许City 的等价物共享ZipCodes。

#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>

using namespace std;

/**
 * Zone Improvement Plan (ZIP) code
 */
class ZipCode {
public:
    ZipCode() : code_(0), plus4_(0) {}
    ZipCode(int code, int plus4 = 0) : code_(code), plus4_(plus4) {}
    virtual ~ZipCode() {};

    int code() const { return code_; }
    int plus4() const { return plus4_; }
    void set_code(int code) { code_ = code; }
    void set_plus4(int plus4) { plus4_ = plus4; }

private:
    int code_;
    int plus4_;
};

typedef boost::shared_ptr<ZipCode> ZipPtr;

/**
 * City points to one or more zip codes
 */
class City {
public:
    const vector<ZipPtr>& zip() const { return zip_; }
    void add_zip_ptr(const ZipPtr x) { if (x != NULL) zip_.push_back(x); }

private:
    // TODO: this vector should be a hash set
    vector<ZipPtr> zip_;
};

/**
 * State contains cities, each of which has pointers to
 * zip codes within the state.
 */
class State {
public:
    const vector<City>& city() const { return city_; }
    const vector<ZipPtr>& zip() const { return zip_; }

    const ZipPtr zip_of(int code) const {
        for (size_t i = 0; i < zip_.size(); i++) {
            if (zip_[i]->code() == code) {
                return zip_[i];
            }
        }
        return ZipPtr();
    }

    void add_city(const City& x) { city_.push_back(x); }
    void add_zip(int code) { zip_.push_back(ZipPtr(new ZipCode(code))); }

private:
    // TODO: these vectors should be hash sets
    vector<City> city_;
    vector<ZipPtr> zip_;
};

int main() {
    State texas;
    City dallas, houston;

    // create state ZIPs
    texas.add_zip(75380);
    texas.add_zip(75381);
    texas.add_zip(77219);
    texas.add_zip(77220);

    // point city ZIPs to the ones we just created
    dallas.add_zip_ptr(texas.zip_of(75380));
    dallas.add_zip_ptr(texas.zip_of(75381));
    houston.add_zip_ptr(texas.zip_of(77219));
    houston.add_zip_ptr(texas.zip_of(77220));

    // print all ZIPs
    cout << "ZIPs in Texas: " << endl;
    const vector<ZipPtr>& zips = texas.zip();
    for (size_t i = 0; i < zips.size(); i++) {
        cout << "    " << zips[i]->code() << endl;
    }
    cout << "ZIPs in Dallas, Texas: " << endl;
    const vector<ZipPtr> zip_ptrs1 = dallas.zip();
    for (size_t i = 0; i < zip_ptrs1.size(); i++) {
        cout << "    " << zip_ptrs1[i]->code() << endl;
    }
    cout << "ZIPs in Houston, Texas: " << endl;
    const vector<ZipPtr> zip_ptrs2 = houston.zip();
    for (size_t i = 0; i < zip_ptrs2.size(); i++) {
        cout << "    " << zip_ptrs2[i]->code() << endl;
    }

    // change a state ZIP...
    cout << "Changing Houston's ZIP 77220..." << endl;
    ZipPtr z = texas.zip_of(77220);
    if (z != NULL) z->set_code(88888);

    // ...and show the ZIPs of the affected city
    cout << "ZIPs in Houston, Texas: " << endl;
    const vector<ZipPtr> zip_ptrs3 = houston.zip();
    for (size_t i = 0; i < zip_ptrs3.size(); i++) {
        cout << "    " << zip_ptrs3[i]->code() << endl;
    }

    return 0;
}

【问题讨论】:

  • 所写的代码不正确:当您将城市或邮政编码添加到某个州时,向量可能需要重新分配自身,此时所有指向现有城市和邮政编码的指针都将失效(因为城市和邮政编码对象将移动到内存中的其他位置)。
  • @James,感谢您指出该错误。我将不得不编辑
  • 由于您的主题是关于避免指针的编程范式,您可能会对以下内容感兴趣:Nobody Understands C++: Part 6: Are You Still Using Pointers?(我喜欢这个吸引人的名字。)我认为它并不全面,但它是一个开始:)

标签: c++ design-patterns pointers


【解决方案1】:

我认为这种情况是两个 1:n 的关系

  1. 州:城市 == 1:n

  2. 城市:邮政编码 == 1:n

基于此,我认为State 包含

vector<ZipCode> zip_;

声音不大。

我可能会这样做

class State {
    vector< City > cities_in_state_;
};

class City {
    vector< Zipcode > zips_in_city_;
};

这不需要指针。

【讨论】:

  • 我同意您在我的示例中对 1:n 的评论(@zneak 实际上也发表了相同的评论)。我试图选择一个比实际的公司专有代码更容易理解的真实示例(在这种情况下,这些类关系是有意义的),但我显然失败了:-\
【解决方案2】:

除非您想复制您的 ZipCode 对象,否则您属于此类使用(在your first link 中描述):

Bar 实例实际上是托管的 通过您程序的其他部分, 而 Foo 类只需要 可以访问它。

这似乎是一种合法的用途。

但是,您可能需要考虑复制选项(如果向量必须重新分配其数据,则永久避免出现问题)或使 State 从其 ZipCodes 聚合 ZipCodes,而不是分发它们 ZipCodes

复制只是意味着您停止使用City 中的指针。聚合ZipCodes 意味着您将给City 提供ZipCode 实例列表,而不是给State 提供ZipCodes 的列表,并且在调用zip_of 时,您将遍历城市并遍历他们的 ZipCode 收藏。

【讨论】:

  • 好的,谢谢。实际上,StateCityZipCode 是一个坏例子(我的坏)。在我的实际代码中,相当于City 可以共享ZipCodes。因此,如果我将City 更改为聚合ZipCodes,则State 中可能存在重复的ZipCodes。
  • @helloworld 与 C++ 共享内容的正确方法是指针或引用。您可以查看 C++0x STL 中的 std::shared_ptrstd::unique_ptr 以提供帮助。在我短暂的一生中,我还没有完成足够多的 C++ 来使用它,但是如果 C++0x 不是一个选项,boost 可能也有类似的东西。
  • boost::shared_ptr 看起来很有希望......谢谢。我可能会用它
猜你喜欢
  • 1970-01-01
  • 2012-03-20
  • 2013-08-09
  • 2015-05-27
  • 2013-11-11
  • 1970-01-01
  • 2021-05-01
  • 1970-01-01
  • 2016-04-17
相关资源
最近更新 更多