父子关系所有权的推荐模式

时间:2014-06-14 01:39:10

标签: c++ design-patterns shared-ptr

我是c ++的新手,我正在尝试理解引用计数内存。在下面的代码中,我有两种返回矩形向量的方法

vector<RectangleRef> &rectanglesRef() { return rects_; }
vector<RectangleRef> rectangles() { return rects_; }

我不确定的是,如果他们中的任何一个在堆栈上为调用者创建了一个新的向量?

当您希望能够公开的向量中包含许多ClassB的ClassA时,建议的模式是什么?你做了吗

void addRectangle(RectangleRef r) { rects_.push_back(r); }
void removeRectangle(RectangleRef r);
vector<RectangleRef> rectangles() { return rects_; }

或者您是否只是让呼叫者访问内部结构并允许他们随意添加/删除内容?

如果Rectangle类要保留指向它所属的Test类的后向指针,则使用

typedef std::weak_ptr<Test> TestWeakRef;
...
TestWeakRef test_;

正确的成语?

感谢您的帮助。

代码:

#include <iostream>
#include <limits>
#include <vector>

using namespace std;

class Point {
public:
    Point() {
        x_ = 0.0;
        y_ = 0.0;
    }

    Point(double x, double y) {
        x_ = x;
        y_ = y;
    }

    double x() const { return x_; }
    double y() const { return y_; }

    void setX(double x) { x_ = x; }
    void setY(double y) { y_ = y; }

    void offset(double dx, double dy) {
        setX(x() + dx);
        setY(y() + dy);
    }

    bool operator == (Point const &point) {
        return x() == point.x() && y() == point.y();
    }

    void operator = (Point const &point) {
        setX(point.x());
        setY(point.y());
    }

private:
    double x_;
    double y_;
};


class Size {
public:
    Size() {
        width_ = 0.0;
        height_ = 0.0;
    }

    Size(double width, double height) {
        width_ = width;
        height_ = height;
    }

    double width() const { return width_; }
    double height() const { return height_; }
    double area() const { return width() * height(); }

    void setWidth(double width) { width_ = width; }
    void setHeight(double height) { height_ = height; }

private:
    double width_, height_;
};

class Rectangle {
public:
    Rectangle() {
        origin_ = Point();
        size_ = Size();
    }

    Rectangle(double x, double y, double width, double height) {
        origin_ = Point(x, y);
        size_ = Size(width, height);
    }

    Point origin() const { return origin_; }
    Size size() const { return size_; }

private:
    Point origin_;
    Size size_;
};

typedef std::shared_ptr<Rectangle> RectangleRef;
typedef std::weak_ptr<Rectangle> RectangleWeakRef;

class Test {
private:
    vector<RectangleRef> rects_;

public:
    Test() {
        rects_ = vector<RectangleRef>();

        for (int i = 0; i < 100; i++) {
            RectangleRef ptr = make_shared<Rectangle>(i*1.0, 0.0, 1.0, 1.0);
            rects_.push_back(ptr);
        }
    }

    vector<RectangleRef> &rectanglesRef() { return rects_; }
    vector<RectangleRef> rectangles() { return rects_; }
};

int main(int argc, const char * argv[]) {

    vector<RectangleRef> r;
    vector<RectangleRef> r1;

    if (true) {
        Test t = Test();

        r = t.rectangles();
        r1 = t.rectanglesRef();

        if (r1 == r) { cout << "they match\n"; }
    }

    // insert code here...
    //std::cout << r->origin().x() << "\n";
    return 0;
}

2 个答案:

答案 0 :(得分:1)

欢迎使用Stack Overflow!你有几个问题有些混乱,但我会尽我所能。


我不确定的是,如果他们中的任何一个在调用者的堆栈上创建了一个新的向量?

这会创建一个完整的新矢量。

vector<RectangleRef> rectangles() { return rects_; }

这可以避免复制。

vector<RectangleRef> &rectanglesRef() { return rects_; }

当您希望能够公开的向量中包含许多ClassB的ClassA时,建议的模式是什么?你做了......或者你只是让呼叫者访问内部结构并允许他们随意添加/删除东西?

常见的范例是像你一样提供访问者功能。我推荐。


如果Rectangle类要保留一个指向它所属的Test类的后向指针,那么正在使用std::weak_ptr正确的习惯用法吗?

这是一个很好的方法,是的。

答案 1 :(得分:1)

德鲁·多尔曼很好地回答了前几个问题。我只是添加了一些东西。

首先,如果您确实需要共享所有权语义,那么您应该只使用shared_ptr。引用计数有开销,如果你没有正确分解循环依赖,它会导致内存泄漏,所以你不应该这样做,除非你真的需要它。

在此代码中,您没有必要存储vector<RectangleRef>而不是简单vector<Rectangle>的真正原因。后者还具有更好的数据局部性,这对于缓存非常重要。

第二,这个

typedef std::weak_ptr<Test> TestWeakRef;
// ...
TestWeakRef test_;

通常不是一个好主意,因为它限制了Test的生命周期管理政策。也就是说,因为weak_ptr只能 引用其生命周期由shared_ptr管理的对象,所以你只能做Test t;unique_ptr<Test> t;之类的事情。 Test。除非你有一个非常非常好的理由,否则不要这样做。

如果你想要后向指针,只需使用一个简单的非拥有原始指针,无论unique_ptr对象是在堆栈上还是由shared_ptr或{{1}管理,它都可以工作或其他一些自定义智能指针:

Test * test_;

如果Rectangle中存储的Test的生命周期可以超过Test的生命周期(只有在您没有它们的独占所有权时才会发生这种情况(例如,存储) shared_ptr对他们而言,这通常是不必要的),你的Test析构函数可能必须经过它存储的Rectangle并清除指针以防止悬空指针。但典型的情况是,当对象被销毁时,与Test对象一起存储的内容将消失,在这种情况下,这是不必要的。