2

I am experiencing a curious case of Object slicing. I am working on project where I need singleton classes so my Base and derived class both are singleton. Following sample case describes my situation.

This is my Base class

// Base.h
class Base
{
    public:

        static Base& base;

        virtual void doSomething(){ cout<<"Base Do Something"<<endl; }

    protected:

        Base();
        virtual ~Base();
        static Base& getBaseInstance();

    private:
};

//Base.cpp
Base::Base()
{
    //ctor
}

Base::~Base()
{
    //dtor
}

Base& Base::getBaseInstance()
{
    static Base object;
    return object;
}
Base& Base::base=Base::getBaseInstance();

This is my Derived Class

class Derived: public Base
{
    public:

    static Derived& derived;
    virtual void doSomething(){ cout<<"Derive Do Something"<<endl; }

  static Derived& getDerivedInstance();
    protected:
        Derived();
        virtual ~Derived();

    private:
};

Derived::Derived()
{
    //ctor
}

Derived::~Derived()
{
    //dtor
}

Derived& Derived::derived=Derived::getDerivedInstance();

Derived& Derived::getDerivedInstance()
{
    static Derived object;
    return object;
}

And finally this is my main Function

int main()
{
    cout << "Hello world!" << endl;
    Base::base.doSomething();
    Derived::derived.doSomething();

    Base::base=Derived::derived;

    Base::base.doSomething();

    Base::base=Derived::getDerivedInstance();

    Base::base.doSomething();

    Base& r = Derived::derived;

    r.doSomething();

    Base::base=Derived::getDerivedInstance();
    Base::base.doSomething();
    return 0;
}

And I am getting following output for this

Hello world!
Base Do Something
Derive Do Something
Base Do Something
Base Do Something
Derive Do Something
Base Do Something

So my question is since object slicing should not work on references, then why I am not able to overwrite Base::base reference I created as static data member of Base class with Derived object? While this works fine on Base& r = Derived::derived;

I mean when I call do something with r.doSomething() I get doSomething of Derived class. But this is not so with

    Base::base=Derived::derived;    
    Base::base.doSomething();

or

    Base::base=Derived::getDerivedInstance();    
    Base::base.doSomething();

Any sort of clarification will be appreciated. Thank You.

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
Dr. Xperience
  • 475
  • 1
  • 5
  • 18

3 Answers3

3

When you do

Base::base=Derived::derived;

You are not setting the base reference to refer to the derived class. This is the assignment operator and all it does is asign the Base part of derived to base.

base is still of type Base and can never change as references can only ever be initialized once and you do that with

Base& Base::base=Base::getBaseInstance();

If you want this reassignment behavior you are going to need to use a pointer type.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
1

A reference is for all purposes the object referred to. Assigning to the reference is to assign to the object referred to. In your case the objects have no data members, so there's no effect.

In particular there's no detectable slicing.

The declaration

Base& r = Derived::derived;

… is a very different case: an initialization, not an assignment.

An initialization makes the reference refer to the specified object.

The reference can't be changed after that.


In other news:

  • The globals (the references) run the risk of static initialization order fiasco. They only serve to nullify the advantage of the singleton getter functions. With the globals the functions are just no-purpose added verbosity.

  • Mutable singletons with data members allow communication between widely separate and seemingly unconnected parts of the code. This makes it hard to rely on any assumptions about current state and about what influences what. That's also why global variables are regarded as Evil™ in every programming language, so use singletons with caution.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • This is just a sample. My actual case have a lot of members in Derived class. Thanks for help. I made such a silly mistake. – Dr. Xperience Jul 22 '16 at 16:22
  • My project only have two singletons. They act as service provider. Though for a driver service provider is eh much overhead. – Dr. Xperience Jul 22 '16 at 16:25
1

Base::base is a static reference. You intialize it here:

Base& Base::base=Base::getBaseInstance();

From this point onwards (i.e. before your code using base is executed), base refers to the base instance (i.e. the static instance declared in Base::getBaseInstance()).

When you then do the assignment, you will not change the reference anymore, but you will copy the object into the object referred by base (which is of type base, hence the slicing !).

A workaround could be to make Base::base a pointer. In this way, you'd be able to change it and point to the derived object. However does this fit with our approach of singleton (because then you'll have the base object somewhere AND the derived object).

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • Re "hence the slicing", there are no data members so there can be no detectable slicing. – Cheers and hth. - Alf Jul 22 '16 at 16:12
  • Thanks mate, I know about pointer approach. Just avoiding it to have minimum number of traces (Intel traces/pre-decoding). Thanks again. – Dr. Xperience Jul 22 '16 at 16:19
  • @Cheersandhth.-Alf there may be no data members, but there are virtual functions. As the assignments is about plain object, the Derived type is lost, so that it's not using the expected virtual functions anymore. I'm not natike english speaker: I use "slicing" [using this definition](http://stackoverflow.com/questions/274626/what-is-object-slicing) when ever some information gets lost, but may be you can propose a better wording. – Christophe Jul 22 '16 at 16:22
  • @Christophe: No, the assignment doesn't affect the virtual functions. Technically one can say that the fact that they're unaffected, is an example of slicing. But it's not detectable, since the effect is no effect at all. – Cheers and hth. - Alf Jul 22 '16 at 16:24
  • @Cheersandhth.-Alf after the assignment base.DoSomething() will invoke `base::DoSomething()` and not `Derived::DoSomething()` as it would to for `derived`. So it's "lost" in the copy. We are playing with the words here – Christophe Jul 22 '16 at 16:30
  • @Christope: There is no change in what `base.DoSomething()` invokes. – Cheers and hth. - Alf Jul 22 '16 at 16:36