【问题标题】:What to do with proxy class copy-assignment operator?如何处理代理类复制赋值运算符?
【发布时间】:2010-06-25 21:12:35
【问题描述】:

考虑以下代理类:

class VertexProxy
{
public:
    VertexProxy(double* x, double* y, double* z)
    : x_(x), y_(y), z_(z) {}

    VertexProxy(const VertexProxy& rhs)
    : x_(rhs.x_), y_(rhs.y_), z_(rhs.z_) {}

    // Coordinate getters
    double x() const {return *x_;}
    double y() const {return *y_;}
    double z() const {return *z_;}

    // Coordinate setters
    VertexProxy& x(double val) {*x_ = val; return *this;}
    VertexProxy& y(double val) {*y_ = val; return *this;}
    VertexProxy& z(double val) {*z_ = val; return *this;}

    VertexProxy& operator=(const VertexProxy& rhs)
    {
        // Should it be this
        x_ = rhs.x_; y_ = rhs.y_; z_ = rhs.z_;

        // or this?
        *x_ = *rhs.x_; *y_ = *rhs.y_; *z_ = *rhs.z_;

        return *this;
    }

private:
    double* x_; double* y_; double* z_;
};

我需要能够重置代理,以便它拥有不同的坐标指针(类似于boost::shared_ptr.reset()。此外,我希望能够将坐标值分配给来自不同代理的坐标值(即@ 987654323@).

在我上面的类中operator=应该是什么意思?复制rhs 的指针(浅拷贝)或rhs 的值?还是我应该将operator= 设为私有并提供两个成员函数以避免operator= 的歧义?

编辑:

好的,这里有一些背景信息。我正在围绕第 3 方 GIS 库(shapelib)编写一个包装器,它将顶点坐标(x、y、z、m)存储在单独的数组(而不是结构数组)中。我的代理类用于使这个数组结构看起来更像一个结构数组。它与自定义顶点迭代器类协同工作,使处理顶点范围变得更加容易。

Shapelib 处理内存管理。我的代理类所做的只是在顶点数据中呈现不同的“视图”。当用户使用我的代理操作顶点坐标时,它实际上是在操作 shapelib 形状对象中的顶点坐标。

【问题讨论】:

  • 这是一个真实的例子吗?如果是,为什么你仍然需要指针?由于您所描述的语义问题,并且因为它很容易引入内存泄漏或悬空指针,因此存储值会更好。
  • 这是一个真实示例的简化版本。我正在围绕一个将 x、y、z、m 坐标存储在单独数组(而不是结构数组)中的第 3 方 GIS 库(shapelib)编写一个包装器。我的代理类与自定义顶点迭代器类协同工作,这使得在 3rd 方库中使用顶点范围变得更加容易。
  • 我的语义问题也出现在代理模式的更“合法”使用中。我想知道在这个问题上流行的智慧是什么。
  • 我刚刚意识到operator== 也有同样的问题。

标签: c++ design-patterns proxy


【解决方案1】:

鉴于您的复制构造函数复制了指针,为了保持一致性,您的复制赋值运算符应该分配指针。

VertexProxy& operator=(const VertexProxy& rhs)
{
    x_ = rhs.x_;
    y_ = rhs.y_;
    z_ = rhs.z_;

    return *this;
}

如果这个(诚然有问题的)代码会非常不一致:

VertexProxy test( const VertexProxy& other )
{
    double tmp1, tmp2, tmp3;
    VertexProxy p1( &tmp1, &tmp2, &tmp3 );
    p1 = other;
    return p1;
}

表现不同:

VertexProxy test( const VertexProxy& other )
{
    double tmp1, tmp2, tmp3; // unused
    VertexProxy p1( other );
    return p1;
}

【讨论】:

  • 啊,感谢您指出这一点。这真的很有意义。我将通过 copy-assignment 复制指针,并提供一个复制值的 assignset 成员函数。我会确保在文档中描述复制分配的语义。
  • Nitpick:VertexProxy p1( tmp1, tmp2, tmp3 ); 应该是 VertexProxy p1( &tmp1, &tmp2, &tmp3 );
  • @Emile Cormier:如果您希望您的对象表现得像值,那么您必须使您的复制分配与您的复制构造函数一致,否则您没有值对象.您的课程开始时更像是VertexPointer,然后是VertexProxy;我不是 100% 确定你的例子,但我认为如果你遵循这个习语,你正在做的作业更自然地是 *p2 = *p1,如果你有另一个真正是代理的课程,你可以这样做。
  • int& 是参考;它基本上不是一个值类型。鉴于此,我认为您的意思并不是“我希望 VertexProxy 表现得像一个值”,但我现在不太确定。
  • 我想我的意思是说我希望 VertexProxy 表现得像一个参考。我正在查看std::bitset::reference 的代码。我认为这对我来说可能比shared_ptr 更好。很抱歉造成混乱。 :(
【解决方案2】:

这很简单。你想让 VertexProxy 像一个指针还是一个值?如果您希望它像一个指针,然后复制指针,如果您希望它像一个值,复制值。没有人能告诉你你的类是一个指针还是一个值(特别是因为你似乎有一些不寻常的东西)。如果您需要更好的建议,我们需要知道实际的双打是什么以及为什么。

快速编辑: 在我看来,实际上,如果你做了取消引用,你就会让它像引用或指针一样。但是,原点保持不变。

【讨论】:

  • 谢谢。我认为我的问题是我需要 VertexProxy 来充当两者(就像客户端代码的值,以及我的管家代码的指针)。我需要考虑一下你所说的,看看我是否能找到一种方法来更清晰地解耦值/指针语义。
  • 如果你的家务是外部的,你可以有使用指针实现的私有方法和使用家务的朋友,然后在所有公共方法中充当一个值。
  • 经过反思,我希望VertexProxy 主要表现为一个值。对于家务(初始化和复制指针),我将使用仅供VertexProxy 的朋友使用的私有成员函数。
  • 呵呵,在看到你的最后一条评论之前,我发表了我的最后一条评论。我们在想同样的事情。
  • 正如 Charles Bailey 指出的那样,我的真正意思是说我希望 VertexProxy 像参考一样公开行事。
【解决方案3】:

std::bitset::reference 的作用类似于我的VertexProxy,可以作为模型使用。

typedef std::bitset<8> Bitset;
Bitset bset1, bset2;
Bitset::reference r1(bset1[3]);
Bitset::reference r2(bset2[3]);
r1 = 1;
r2 = r1;
std::cout << "bset2 = " << bset2 << "\n";

r2 = r1 以上复制值。

【讨论】:

  • 是的,我根据 FSF 的 vector&lt;bool&gt;bitset 的快速读取来模拟我的代理 - 然后意识到如果 RHS 是同一类型并且您没有覆盖默认的复制分配,这真的会破坏事情。我在这里做了非常像你的事情 - 明确定义 operator=(T const &amp;) 仅复制值。当然,拥有 STL 模型很有用,然后从其他做过同样事情的人那里追溯确认 :)
【解决方案4】:

我会说这取决于对象有多大。

如果代理主题非常大,那么使用引用计数共享指针是可行的方法。只需在复制操作中复制共享指针即可。

如果不是那么大,那么深拷贝会更好。为每个人减少麻烦。

【讨论】:

  • 对不起,我的问题似乎还不够清楚。我需要能够复制指针(浅拷贝)和复制值。问题是,operator= 应该复制指针还是值?另请注意,我的顶点的内存管理由另一个库处理(请参阅我的问题下的评论)。
  • @Emile:如果它令人困惑,那么根本不允许赋值运算符或复制构造函数。而是制作描述性函数。类似于 copy_shallow() 和 copy_deep()。
  • 我决定让 VertexProxy 公开表现得像一个值。为了管理指针,我将使用私有成员函数。
猜你喜欢
  • 2017-02-17
  • 1970-01-01
  • 1970-01-01
  • 2021-07-12
  • 1970-01-01
  • 2017-11-21
  • 1970-01-01
  • 2012-02-01
  • 2018-11-13
相关资源
最近更新 更多