简体   繁体   中英

How do I assign a data object with const members?

Hope this is not a duplicate. If so, please point me to it in a comment and I'll remove the question again.

I have a data object with data that's only valid in a bundle - ie there's no sense in changing the value of one member without invalidating the other members.
This data object describes some image information:

struct ImageInfo
{
    ImageInfo(const double &ppix, ...)
        : PpiX(ppix),
        ...
    { }

    const double PpiX;
    const double PpiY;
    const int SizeX;
    const int SizeY;
};

In my image object I have a non-const member of type ImageInfo :

class MyImageObject
{
    ...
private:
    ImageInfo mMyInfo;
}

I want to be able to change mMyInfo at runtime, but only so that it will take a new ImageInfo(...) instance.

In the MyImageObject::Load() function, I'd like to read this data from the file info and then create an ImageInfo instance with the correct set of data:

double ppix = ImageFile.GetPpiX();
...
mMyInfo = ImageInfo(ppix, ...);

But I couldn't manage to write a valid assignment operator (copy constructor is possible of course). My solution left mMyInfo empty, because I didn't reference this :

ImageInfo operator=(const ImageInfo &other)
{
    // no reference to *this
    return ImageInfo(other);
}

Out of curiosity I'd like to know how the assignment operator for such a class would need to look like.

I'm using plain C++.

EDIT
Possible solutions (the goal is to keep the data transportable, but coherent):

  • Use private members together with Get...() functions -> simple, but I'd like to avoid the parentheses.
  • Store a pointer to ImageInfo: ImageInfo *mpMyInfo; (I'd like to avoid the heap.)
  • Use serialization and store the serialized ImageInfo, then create local instances from the serialized data.

I don't think you can have const member variables that aren't static. If you need const variables that change with the instance, you could do something like this:

struct ImageInfo
{
private:
    double myPpiX;
    double myPpiY;
    int mySizeX;
    int mySizeY

public:
    ImageInfo(const double &ppix, ...)
        : myPpiX(ppix),
          PpiX(myPpiX),
        ...
    { }

    ImageInfo( const ImageInfo &other)
    : myPpiX( other.myPpiX),
      PpiX(myPpiX)
     ...
    { }

    const double &PpiX;
    const double &PpiY;
    const int &SizeX;
    const int &SizeY;

    // EDIT: explicit assignment operator was missing
    ImageInfo& operator=(const ImageInfo &other)
    {
        myPpiX  = other.myPpiX;
        myPpiY  = other.myPpiX;
        mySizeX = other.mySizeX;
        mySizeX = other.mySizeX;
        return *this;
    }
};

The values are stored in the private variables that can be set at construction, and their values accessed by const references. You're also not dependent on the references passed into the constructor living as long as the ImageInfo instance.

As they are data fields that can be modified, they are not const .

If you want to restrict post-construct access to them to const , you need to wrap them in accessors as follows:

struct ImageInfo
{
  ImageInfo(const double &ppix, /*...*/)
    : PpiX_(ppix),
    /*...*/
  { }
  double const& PpiX() const {return PpiX_; };
  double const& PpiY() const {return PipY_; };
  int const& SizeX() const {return SizeX_; };
  int const& SizeY() const {return SizeY_; };
private:
  double PpiX_;
  double PpiY_;
  int SizeX_;
  int SizeY_;
};

That allows move/copy assignment and construction, while blocking non- const access outside of said construction.

Avoiding the () is tricky, but could be done with pseudo-references, something like this:

struct pseudo_const_reference_to_Ppix {
  ImageInfo const* self;
  operator double() const { return self->Ppix; }
  void reseat( ImageInfo const* o ) { self = o; }
};

plus a whole pile of boilerplate to overload every const operator on the left and right such that the above pseudo_const_reference_* is just as valid as double .

Generic versions can be written (either taking a functor or a std::function if you are willing to suffer type erasure overhead).

Then you maintain these pseudo- const references on assignment and copy/move construction.

I think the () is the better option.

Note that the overhead of a pointer (or more) per pseudo-reference is basically unavoidable: member variables do not have access to the this from which they are invoked, even though the accesing site has it right there plain as day.

If something is const, you can't change it. Full stop.

So you must adjust the design somewhere, either not have those ImageInfo members const, not have ImageInfo as member, or best: not do the assignment.

Normally const members are set in constructor. You can make a load function that creates a MyImageObject object with all its content, so avoiding to have a half-done thing and load the sate in a second phase.

An alternative is to have the mMyInfo indirectly, say using unique_ptr, then you can replace it with another instance. I would not do that without a really good reason.

Immutable value objects are great. But the variable holding the value object (mMyInfo in MyImageObject) should be (non-constant) a pointer. In other languages (eg Java) this is automatically the case, but not in C++, where you need the * operator. Also, there is no need to override/implement the = operator for value objects. To change the image data within the image object, you assign a newly constructed ImageInfo object to the myImageInfo pointer. This way, none of the internal variables of the value object are changed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM