slicing/vptr/AOB — pbref between base^derived

(For vptr during slicing, see other posts)

Q: Any Slicing when func1(const B& b) receives a D argument?
A: no, since there’s just _one_ object (see posts on implicit cloning). But not sure afterwards.

Background — On the “real estate” of the Single-Inheritance D object, there is a B object, like a basement. B’s real estate is part of D’s real estate — these 2 objects share[3] the same “postal address”, making pointer casting possible.

[3] with multiple inheritance, Derived object and the embedded Base sub-object don’t necessarily share the same postal address of “this”

(Warning: AOB is inapplicable for multiple-inheritance. See other posts for details.) In the opening question, when you get a B ref to a D object, you get the address of the basement (AOB). Using this B reference, you can access B’s fields only. To access D’s fields, you must downcast a B ptr into a D ptr. AOB is the basis of most if not all slicing problems such as

* copy ctor qq( B b=d ) — In the implicit copier call, the implicit param “const B&” gets the AOB so can only access the B fields of the D object. After the copier call, the LHS becomes a sliced copy of the D.

* assignment qq( B b; b=d ) — The param “const B&” gets the AOB so can only access the B fields of the D object. LHS becomes a sliced copy of the D.

Remember the onion — slice off the outer layer.

vptr is a const non-static field – (slicing, casting)

When you “cast” a c++ or java POINTER[1] up and down family tree, the vptr field of the pointee persistently points to the “most derived” vtbl. To fully appreciate the constness of the vptr, consider slicing —

Q: during slicing, how does vptr end up pointing to the base vtbl?
A: copy constructor of the base object will be used, which (blindly) initializes vptr with the base object’s vtbl.

Background — vtbl is an array of func pointers. One vtbl per CLASS, but —

Q: Is there any compiler where superclass’s and subclass’s vtbl share the same address?
A: Won’t work. When slicing, the trim-down object’s vptr must NOT point to original vtbl. Trim-down object can never access derived class methods.
A: but what if a subclass overrides no method? Probably will share the vtbl with super class?

vptr is like a non-static const field. In the non-slicing scenario, The magic of polymorphism depends on the “vptr still pointing to subclass vtbl even if object is accessed by a base ptr”.

A twist on the constness — After this->vptr is initialized in the base ctor, it gets reseated during subclass ctor. During destruction, this->vptr gets reseated during Base dtor. This knowledge is relevant only if base ctor/dtor calls virtual methods.

[1] nonref won’t work.

Changi(citi)c++IV #onMsg dispatcher, STL, clon`#done

I feel this is more like a BestPractice (BP) design question.

void onMsg(Price& const price){ // need to persist to DB in arrival order
// Message rate is very high, so I chose async producer-consumer pattern like
  dispatch(price);
}

// sharding by symbol name is a common practice

// %%Q: should I dispatch to multiple queues?
// A: Requirement says arrival order, so I decided to use a single queue.

// The actual price object could be a StockPrice or an OptionPrice. To be able to persist the option-specific attributes, the queue consumer must figure out the actual type.

// Also, price object may be modified concurrently. I decided to make a deep copy of the object.

// Q: when to make a copy, in onMsg or the consumer? I personally prefer the latter, but let’s say we need to adopt the former. Here’s a poor solution —

//Instead of dispatch(price)
dispatchPBClone(price); // slicing a StockPrice down to a Price. Here’s my new solution —

dispatchPBPointer(price.cloneAndReturnPtr());

// clone() is a virtual method in StockPrice and OptionPrice.
//Q: clone() should allocate memory on the heap, but where is the de-allocation?
//A: Use smart ptr.

slicing – [[c++ primer]]

Update — c# has no slicing because the only composite type to be passed by cloning is struct but structs is never subclassed. Java is even cleaner. C# class/array/Delegate instances are like java objects — always “pbref” rather than “pbclone” i.e. pass-by-value.

Slicing is the #1 pitfall in c++ inheritance, subsumption, pass-by-value (pbclone — see other posts). It’s common and tricky.

class Base{};
class Derived : public Base {};
Base b;
Derived d;

Q: when do we see slicing of a big object into a small object?
A: assigning qq(b = d). The LHS base object has a smaller real estate than the RHS subclass object
A: copying qq[Base b = d] or qq[Base b(d)]
A: {special case} when pbclone passing a child object into a parent parameter, essentially copying
A: a container with nonref type arg qq[ myMultiset.insert(d) ]
A: in all these cases, the LHS object is a parent object — sufficient memory to contain parent’s fields — no room for child’s toys:).

Q: When can we avoid slicing by sticking to just one object? Note copier/assignment always involve 2 objects — always slicing.
A: pbref using ref or ptr, bypassing field-by-field cloning. However, you can’t avoid slicing completely. If i have an object of type D, to be passed into a field [2] of type B, I get slicing of some sort always, since i can’t access the child portion of the D object, unless i dynamic_cast.

[2] or stackVar incl params

onion — slice off the outer layer.

See [[c++ primer]] P578

STL container to hold Child objects +! pointers – slicing

http://ootips.org/yonat/4dev/smart-pointers.html — but slicing problem!

#include
#include
struct Base {
    virtual void print() const{cout<<1<<"\n";}
    virtual ~Base(){}
};
struct Derived : public Base {
    virtual void print() const{cout<<2<<"\n";}
};
int main(){
    Base b;
    Derived d;
    std::vector v;
 
    v.push_back(b); // OK
    v.push_back(d); // no error but not sure if there’s any hidden issue
    cout<<  v.size() <<endl; // prints 2
    std::vector::const_iterator pos;
    for(pos = v.begin(); pos != v.end(); ++pos){
       pos->print(); // Der object sliced!
    }
}

default assignment is bitwise copy – c/c++ vs java

in C language, A = b would copy value of b into A’s memory location, resulting in 2 detached clones. Consider a C struct — the copying is bitwise.

java primitives behave the same but Java reference variables don’t. A = b would make A a “hard link” of b, so A and b point to the same object[2]. Exactly like c++ ptr copy. Note ptr A and ptr b must be compatible types. If A is a base type then slicing occurs.

Note java cloning, c/c++ assignment, c++ copier are all field by field. [2] is not field by field.

C++ treats all data types (float, char, classes…) as objects, but default assignment always works as in C (and Java primitives). Even if A and b are class types, A = b would copy value of b into A’s memory location, resulting in 2 detached clones, without creating a new object. Actually qq(A = b) may call the copier without the “explicit” modifier on the copier.

Bitwise copy means something special for a ptr field — the 4-byte content (ie address of pointee) is copied.