简体   繁体   中英

How to utilize a public constant reference to a private (struct) member for code encapsulation

In my buggy class, I'm only exposing a constant version of MyStruct , experimenting with initializing a constant reference to the private member in an initializer list.

The program I'm debugging is functionally equivalent to the following:

#include <iostream>
#include <optional>

struct MyStruct {
  int member = 1;
};

class MyType {
  MyStruct struct_member_;
public:
  MyType() : struct_member(struct_member_) {}
  MyType& operator =(const MyType& other_side) {
    struct_member_ = other_side.struct_member_;
    return *this;
  };
  const MyStruct& struct_member;
  void test() const {
    std::cout << "Why isn't " << &struct_member << " the same as " << &struct_member_ << std::endl;
  }
};

int main()
{
    std::optional<MyType> my = MyType();
    my.value().test();

    std::optional<MyType> yours = MyType();
    yours.value().test();

    my = yours;
    my.value().test();

    my = MyType();
    my.value().test();

    return 0;
}

For this program, here's the output:

Why isn't 0x7ffeefbff890 the same as 0x7ffeefbff8a0
Why isn't 0x7ffeefbff868 the same as 0x7ffeefbff878
Why isn't 0x7ffeefbff890 the same as 0x7ffeefbff8a0
Why isn't 0x7ffeefbff890 the same as 0x7ffeefbff8a0

However, in the program I'm debugging, struct_member_ and struct_member are getting desynchronized (either the const reference struct_member is getting assigned to a different place in memory-- except it's const, or it's loosing track with an updated struct_member_ -- except its memory address shouldn't change?) and I'm not sure which or why.

Any ideas on what could lead to this happening, or tips to make this pattern work with non-POD types? (I plan to be transitioning to method-based accessors anyway, since this experiment seems to be failing.)

Turns out optionals and copy constructors were key to all of this. The copy constructor was defaulting and I didn't realize it. Funny, because the compiler warned that I needed a non-default operator= , but didn't afford me the same warning for the copy constructor.

class MyType {
  MyStruct struct_member_;
public:
  MyType() : struct_member(struct_member_) {
    std::cout << "constructor" << std::endl;
  }
  MyType(const MyType& other) : struct_member(struct_member_) {
    std::cout << "copy constructor " << std::endl;
    struct_member_ = other.struct_member_;
  };
  MyType& operator =(const MyType& other_side) {
    std::cout << "assignment operator called" << std::endl;
    struct_member_ = other_side.struct_member_;
    return *this;
  };
  const MyStruct& struct_member;
  void test() const {
    std::cout << "Why isn't " << struct_member.member << ": " << &struct_member << " the same as " << &struct_member_ << std::endl;
  }
};

Yields the correct output:

constructor
copy constructor 
Why isn't 0x7ffeefbff8a0 the same as 0x7ffeefbff8a0
constructor
copy constructor 
Why isn't 0x7ffeefbff878 the same as 0x7ffeefbff878
assignment operator called
Why isn't 0x7ffeefbff8a0 the same as 0x7ffeefbff8a0
constructor
assignment operator called
Why isn't 0x7ffeefbff8a0 the same as 0x7ffeefbff8a0

Altogether, it'd be less hassle just to expose getters as methods.

Either delete the copy constructor (because the default behavior binds the reference to the "wrong" thing in this case):

MyType(MyType const&) = delete;

Or provide a copy constructor that does the right thing, by binding the public const accessor to the self-same object:

MyType(MyType const& other)
  : struct_member_{other.struct_member_}
  , struct_member(struct_member_) {}

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