1

I just encountered a snippet of code which seems quite strange to me(see below as a minimal example), the derived::base is a reference to another object of type of base, can someone help me to answer the questions in the comments?

class base{
public:
   int a;
   int b;
};

class derived : public base{
public:
   double c;
   void run(const base & bs){
     ((base &) *this) = bs; // what does this line do? 
                            // Is derived::base now a copy of bs? 
                            // If yes, but why not write ((base) *this) = bs?
                            // if not, then derived::base is a reference to bs, 
                            // then does it mean the memory of derived::base
                            // and members of derived are no longer contiguous?
     std::cout << "a = " << a << std::endl;

   }
};

PS

comments by @LightnessRacesinOrbit helped a lot to clear the questions, but I can only accept an answer post, the best is by @WhiZTiM

Allanqunzi
  • 3,230
  • 1
  • 26
  • 58
  • 1
    All it does is invoke the copy constructor of the base class which will copy the the members of the base class (i.e. `a` and `b`) without touching anything in the derived part of the object (i.e. `c`). I don't see why the memory wouldnt be contiguous anymore. – imreal Feb 12 '17 at 17:44
  • afaik you never had the guarantee that the objects members are stored in contiguous memory, even without this "copy only the base class members" trick. Maybe there is some misunderstanding on the meaning of "contiguous". Can you explain a bit more what exactly worries you about that code? – 463035818_is_not_an_ai Feb 12 '17 at 17:49
  • @imreal, but why not write `((base) *this) = bs` if invoking the copy constructor? – Allanqunzi Feb 12 '17 at 17:57
  • [related](http://stackoverflow.com/questions/4177346/class-contiguous-data), [also related](http://stackoverflow.com/questions/15430848/are-class-members-garaunteed-to-be-contiguous-in-memory) – 463035818_is_not_an_ai Feb 12 '17 at 17:58
  • @tobi303, my worry is that if it is just invoking the copy constructor, why not just write `((base) *this) = bs` or `derived::base = bs`? – Allanqunzi Feb 12 '17 at 18:01
  • not sure, but I think `((base) *this) = bs;` would be just fine as well. Did you try it? Is this a typo: `base = bs` ? `base` is a type in the example, not an object – 463035818_is_not_an_ai Feb 12 '17 at 18:03
  • also `derived::base = bs` would not compile – 463035818_is_not_an_ai Feb 12 '17 at 18:04
  • @tobi303, `((base) *this) = bs` gives different behavior, and any reason why `derived::base = bs` doesn't compile? – Allanqunzi Feb 12 '17 at 18:07
  • @imreal: _"All it does is invoke the copy constructor of the base class"_ No, it doesn't. – Lightness Races in Orbit Feb 12 '17 at 18:11
  • Clever solution to copying an object … For exactly that reason seeing this in a code review would immediately worry me. It reeks of being too clever and may very well be a symptom of a deeper design problem. I’d insist on a rock solid (and documented!) reason why such acrobatics are unavoidable. – besc Feb 12 '17 at 21:22

2 Answers2

2
void run(const base & bs){
     ((base &) *this) = bs; 
     std::cout << "a = " << a << std::endl;
}

The above code can be broken down as:

void run(const base & bs){
     base& base_of_this_instance = *this; 
     base_of_this_instance = bs;
     std::cout << "a = " << a << std::endl;
}

The memory for an object of derived may be laid out as:

||  int a  |x|  int b  |y|  int c  ||   // <- x and y represents some hypothetical padding
||        base         |y|         ||   // <- We can slice `derived` and retrieve only base
||           derived               ||   // <- Memory consumed by derived

In your derived::run method, first, a reference to the base portion of derived is obtained, secondly that base is assigned to bs. This assignment will invoke the copy assignment operator of base. That means the base portion will now now hold a copy of whatever was in bs.

WhiZTiM
  • 21,207
  • 4
  • 43
  • 68
  • What differences does if make if it is written as `((base) *this) = bs`? – Allanqunzi Feb 12 '17 at 18:09
  • @Allanqunzi: You get a useless temporary copy instead. – Lightness Races in Orbit Feb 12 '17 at 18:12
  • `((base) *this) = bs` will simply have `bs` assigned to a sliced portion `base` of derived. Since that *sliced* portion is a temporary unnamed object, it will be destructed at the end of the `;`. In effect you get wasted work. `derived` will not be affected – WhiZTiM Feb 12 '17 at 18:13
  • @LightnessRacesinOrbit, why it is temporary? I thought `*this` would be a reference to the current class. – Allanqunzi Feb 12 '17 at 18:14
  • @Allanqunzi: Because that's what casting does. `(base)` is a cast. – Lightness Races in Orbit Feb 12 '17 at 18:16
  • @Allanqunzi And actually `*this` is _not_ a reference, though it does refer to the original object in much the way a reference would. – Lightness Races in Orbit Feb 12 '17 at 18:16
  • @LightnessRacesinOrbit, thanks I think now I get most of it except the part "*this is not a reference" – Allanqunzi Feb 12 '17 at 18:18
  • @Allanqunzi, well, [`this`](http://en.cppreference.com/w/cpp/language/this) itself is a `prvalue` expression, [dereferencing it](http://en.cppreference.com/w/cpp/language/operator_member_access#Built-in_indirection_operator), produce the `lvalue` of the original object. This is akin to using `d` after creating `d` like `derived d;` – WhiZTiM Feb 12 '17 at 18:28
  • @Allanqunzi: Dereferencing a pointer gives you an lvalue `T`, not an lvalue `T&`. It's a technical detail that you don't need to worry about. – Lightness Races in Orbit Feb 12 '17 at 18:28
0

The result of

((base &) *this)

is references to base class

You can save it to a variable:

base& refToBase = ((base &) *this);

refToBase is a reference to the same object which is this

After that you have assignment

refToBase = bs;

It will assign bs's value to refToBase object

Like this:

int i = 10;
int p = 20;
int& refI = i;
int& refP = p;
refI = refP; // i == 20
p = 15; // p ==15 but i == 20

So after "strange code" from your example are executed we have the copies of bs.a and bs.b in derived::a and derived::b

the memory of derived::base and derived::c is still one batch

MaxV
  • 2,601
  • 3
  • 18
  • 25