views:

58

answers:

4

Consider the following proxy class:

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_;
};

I need to be able to reset the proxy so that it holds different coordinate pointers (similarly to boost::shared_ptr.reset(). In addition, I would like to be able to assign the coordinate values to the ones from a different proxy ( i.e. proxy1.assign(proxy2) ).

What should be the meaning of operator= in my class above? To copy rhs's pointers (shallow copy) or rhs's values? Or should I just make operator= private and provide two members functions to avoid the ambiguity of operator=?

EDIT:

Ok, here's some background information. I'm writing a wrapper around a 3rd party GIS library (shapelib), which stores vertex coordinates (x,y,z,m) in separate arrays (instead of an array of structs). My proxy class is used to make this struct of arrays appear more like an array of structs. It works in tandem with a custom vertex iterator class that makes it much easier to work with ranges of vertices.

Shapelib handles the memory management. All my proxy class does is present a different "view" into the vertex data. When the user manipulates vertex coordinates using my proxy, it actually manipulates the vertex coordinates in the shapelib shape object.

A: 

I would say that it depends on how large the object is.

If the proxy subject is very big then using a reference counting shared pointer is the way to go. Just copy the shared pointer in the copy operation.

If it isn't that big then a deep copy is better. Less hassle all around for everyone.

Zan Lynx
I'm sorry, it seems I have not made my question clear enough. I need to be able to both copy the pointers (shallow copy) and copy the values. The question is, should operator= copy the pointers or the values? Also note that the memory management for my vertices is handled by another libary (see my comment under my question).
Emile Cormier
@Emile: If it is confusing then do not allow the assignment operator or copy constructor at all. Instead make descriptive functions. Something like copy_shallow() and copy_deep().
Zan Lynx
I decided to make VertexProxy publicly behave like a value. For housekeeping of the pointers, I'll use private member functions.
Emile Cormier
+1  A: 

It's pretty simple. Do you want VertexProxy to act like a pointer, or a value? If you'd rather it acted like a pointer, then copy the pointers, if you'd rather it acted like a value, copy the values. Nobody can tell you that your class is a pointer or a value (especially since you seem to have something somewhat unusual). If you want better advice, we'd need to know what holds the actual doubles and why.

Quick edit: Seems to me like actually, if you did the dereference, you'd have it acting like a reference or a pointer. However, the original point remains the same.

DeadMG
Thanks. I think my problem is that I need VertexProxy to act like both (like a value for the client code, and like a pointer for my housekeeping code). I'll need to think about what you said for a little while, and see if I can find a way to decouple the value/pointer semantics more cleanly.
Emile Cormier
If your housekeeping is external, you could have private methods that use the pointer implementation and friend the housekeeping, then act like a value in all public methods.
DeadMG
Upon reflection, I want `VertexProxy` to act primarily like a value. For housekeeping (initializing and copying the pointers), I'll use private member functions to be used only by friends of `VertexProxy`.
Emile Cormier
Hehe, I posted my last comment before I saw your last comment. We were thinking the same thing.
Emile Cormier
As Charles Bailey pointed out, I really meant to say that I want VertexProxy to publicly act like a reference.
Emile Cormier
+2  A: 

Given that your copy constructor copies the pointers, for consistency your copy-assignment operator should assign the pointers.

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

    return *this;
}

It would be very inconsistent if this (admittedly questionable) code:

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

acted differently to:

VertexProxy test( const VertexProxy& other )
{
    double tmp1, tmp2, tmp3; // unused
    VertexProxy p1( other );
    return p1;
}
Charles Bailey
Ah, thanks for pointing that out. This really makes sense. I'll make copy-assignment copy the pointers, and provide an `assign` or `set` member function that copies the values. I'll make sure to describe the semantics of copy-assignment in the documentation.
Emile Cormier
Nitpick: `VertexProxy p1( tmp1, tmp2, tmp3 );` should be `VertexProxy p1( `
Emile Cormier
@Emile Cormier: Thanks, edited. Or even `reset` if you're following the shared pointer idiom.
Charles Bailey
To be consistent with shared_ptr, I'll provide a `reset` that's synonymous with copy-assignment. It'll also give the option for client code to be more explicit in it's intent.
Emile Cormier
Upon further reflection, I think I actually need to make copy-construction inconsistent with copy-assignment. I want `VertexProxy` to behave like a value, but I'd also like client code to be able to copy-construct their own local proxy. For example: `VertexProxy p1(*vertexIterator1); VertexProxy p2(*vertexIterator2); p1.x(12.3); p2 = p1;` Your answer is still really helpful.
Emile Cormier
@Emile Cormier: If you want your objects to behave like values then you *have* to make your copy-assignment consistent with your copy constructor otherwise you don't have value objects. Your class started off more like a `VertexPointer` then `VertexProxy`; I'm not 100% sure about your example, but I think if you follow the idiom the assignment that you are doing would more naturally be `*p2 = *p1` which you could do if you had another class that really was a proxy.
Charles Bailey
@Charles Bailey: Gotcha. I'll try renaming VertexProxy into VertexPointer, and add a VertexProxy class that'll have references to x,y,z. I'll see how that works out.
Emile Cormier
@Charles Bailey: Wait, what about: `int a=c;` Those are not consistent, yet it is the behavior I'd like to mimic with my proxy class. Perhaps I really want VertexProxy to have *reference* semantics?
Emile Cormier
`int it's fundamentally not a value type. Given this, I don't think you really meant "I want VertexProxy to behave like a value" but I'm not really sure now.
Charles Bailey
I guess I meant to say that I wanted VertexProxy to behave like a reference. I'm looking through the code for `std::bitset::reference`. I think that might be a better idiom for me than `shared_ptr`. Sorry about the confusion. :(
Emile Cormier
A: 

std::bitset::reference performs a role similar to my VertexProxy and can be used as a model.

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 above copies values.

Emile Cormier