Overriding virtual operator of parent class [solution with dynamic_cast]

Below is simplified code consists of two classes, namely Parent and Child.
Child is inherited from Parent.

All member functions of class Parent are declared virtual, and they have been overridden in the class Child.

Code 1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <cstdlib>
#include <iostream>
using namespace std;

#define QUANTITY 5


class Parent
{
    protected:
        int ID;

    public:
        virtual void showID()                      { cout << "Parent::showID()" << endl << endl; }

        virtual void operator= ( const Parent &p)  { this->ID = p.ID; cout << "Parent::operator=() invoked."<< endl << endl;  }
};


class Child : public Parent
{
    public:

        Child()                           { this->ID = rand(); }

        void showID()                     { cout << "Child::showID() -- ID is " << this->ID << endl << endl; }

        void operator= ( const Child &p)  { this->ID = p.ID; cout << "Child::operator=() invoked."<< endl << endl;  }
};


Parent *array[QUANTITY];


int main()
{
    for (int i=0; i < QUANTITY; i++)
        array[i] = new Child;

    for (int i=0; i < QUANTITY; i++)
        array[i]->showID();

    *array[2] = *array[3];

    for (int i=0; i < QUANTITY; i++)
        array[i]->showID();

    for (int i=0; i < QUANTITY; i++)
        delete array[i];

    return 0;
}


The output of the code:

Child::showID() -- ID is 1804289383

Child::showID() -- ID is 846930886

Child::showID() -- ID is 1681692777

Child::showID() -- ID is 1714636915

Child::showID() -- ID is 1957747793

Parent::operator=() invoked.

Child::showID() -- ID is 1804289383

Child::showID() -- ID is 846930886

Child::showID() -- ID is 1714636915    

Child::showID() -- ID is 1714636915

Child::showID() -- ID is 1957747793



Question:

Why is Parent::operator= invoked instead of Child::operator= ..?

Isn't it already declared virtual and hence would be overridden..?

I need to invoke Child::operator= instead. How to achieve this?
Last edited on
closed account (D4S8vCM9)
I apparently you only hide the Parent::operator=.
Try using using Parent::operator=; inside Child.

EDIT: tried it here http://ideone.com/OfZlGi, but need to have an account to edit :-/
Sadly I have no time currently, because I'm running a build of my projects which is about to be ready in the next seconds :-/
Last edited on
Why is Parent::operator= invoked instead of Child::operator= ..?

The main issue is that the Child::operator= has a different signature than the Parent::operator=, so the Child::operator= is not virtual and doesn't override the Parent version. It simply hides it when you're dealing with objects who's static type is Child.


Isn't it already declared virtual and hence would be overridden..?

For a function be overriden, the parameter list in the overriding function must be identical.


I need to invoke Child::operator= instead. How to achieve this?

Cast to the correct type before invoking the operator. You probably need to consider whether a Child being assigned to a Parent is meaningful and vice versa. operator= of a derived class typically calls the base class operator=.
Last edited on
I am not sure, but I think that
virtual void operator= ( const Parent &p)
is not ovverriden by virtual void operator= ( const Child &p)

if you want to override your customized operator= in Child perhaps you have to declare a void operator= ( const Parent &p)

Question: how did you be able to compile this program? I remember that when I tried to do a thing like that my compiler (I use gcc) returned me an error (that says that you are declaring an already implicitly declared function).... (this why I never more tried to overload operator= )
Last edited on
try adding one more operator= in Child class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

class Child : public Parent
{
    public:

        Child()                           { this->ID = rand(); }

        void showID()                     { cout << "Child::showID() -- ID is " << this->ID << endl << endl; }

        void operator= ( const Child &p)  { this->ID = p.ID; cout << "Child::operator=() invoked."<< endl << endl;  }

// This overrides Parent::operator=

void operator= ( const Parent &p)  
   { 
         try{
                   const Child & ch = dynamic_cast<const Child&>(p);
                    this->ID = ch.ID; 
                   cout << "Child::operator=() invoked."<< endl << endl;  
              }

        catch( std::bad_cast )
           {
                // processing exception
           }

          
   }
};
Last edited on
Thanks TTT.
Your solution of using dynamic_cast indeed works.

Meanwhile I've found another workaround, without using dynamic_cast.
It seems working fine, as below:

code 2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <cstdlib>
#include <iostream>
using namespace std;

#define QUANTITY 5


class Parent
{
    protected:
        int ID;

    public:
        int getID()                         { return this->ID; }

        virtual void showID()               { cout << "Parent::showID()" << endl << endl; }

        virtual void operator= (Parent &p)  { this->ID = p.ID; cout << "Parent::operator=() invoked."<< endl << endl;  }
};


class Child : public Parent
{
    public:

        Child()                      { this->ID = rand(); }

        void showID()                { cout << "Child::showID() -- ID is " << this->ID << endl << endl; }

        void operator= (Parent &p)   { this->ID = p.getID();  cout << "Child::operator=() invoked."<< endl << endl; }
};


Parent *array[QUANTITY];


int main()
{
    for (int i=0; i < QUANTITY; i++)
        array[i] = new Child;

    for (int i=0; i < QUANTITY; i++)
        array[i]->showID();

    *array[2] = *array[3];

    for (int i=0; i < QUANTITY; i++)
        array[i]->showID();

    for (int i=0; i < QUANTITY; i++)
        delete array[i];

    return 0;
}


The output of this code:

Child::showID() -- ID is 1804289383

Child::showID() -- ID is 846930886

Child::showID() -- ID is 1681692777

Child::showID() -- ID is 1714636915

Child::showID() -- ID is 1957747793

Child::operator=() invoked.

Child::showID() -- ID is 1804289383

Child::showID() -- ID is 846930886

Child::showID() -- ID is 1714636915

Child::showID() -- ID is 1714636915

Child::showID() -- ID is 1957747793


Questions:

1). Program output seems fine. The code is correct, right..?

2). Meanwhile, one problem is:- I have to remove the keyword const from the argument of void operator= (Parent &p)

If I use void operator= (const Parent &p) instead, then this error occurs during compilation:

error: passing 'const Parent' as 'this' argument of 'int Parent::getID()' discards qualifiers [-fpermissive]

I don't know why and don't understand the error message.
Could anyone explain this..?

Thanks.
Last edited on
1). Program output seems fine. The code is correct, right..?

Seems safe enough, as is. But it's going to make getting operator= corrrect in further derived classes difficult.

If I use void operator= (const Parent &p) instead, then this error occurs during compilation:

That's because Parent::getID() is not const correct. It should be int getID() const and Parent::showID and Chlid::showID should follow suit.
Last edited on
Thanks Cire.
After putting back keyword const (line 14, line 18, line 30 below), the revised code becomes:

Code 3:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <cstdlib>
#include <iostream>
using namespace std;

#define QUANTITY 5


class Parent
{
    protected:
        int ID;

    public:
        int getID() const                         { return this->ID; }

        virtual void showID()                     { cout << "Parent::showID()" << endl << endl; }

        virtual void operator= (const Parent &p)  { this->ID = p.ID; cout << "Parent::operator=() invoked."<< endl << endl;  }
};


class Child : public Parent
{
    public:

        Child()                          { this->ID = rand(); }

        void showID()                    { cout << "Child::showID() -- ID is " << this->ID << endl << endl; }

        void operator= (const Parent &p) { this->ID = p.getID();  cout << "Child::operator=() invoked."<< endl << endl; }
};


Parent *array[QUANTITY];


int main()
{
    for (int i=0; i < QUANTITY; i++)
        array[i] = new Child;

    for (int i=0; i < QUANTITY; i++)
        array[i]->showID();

    *array[2] = *array[3];

    for (int i=0; i < QUANTITY; i++)
        array[i]->showID();

    for (int i=0; i < QUANTITY; i++)
        delete array[i];

    return 0;
}


This works, its output is exactly same with the output of code 2 above.


Questions:

1). Could I say that this approach is safer than using dynamic_cast?
dynamic_cast seems a bit indeterministic during run-time.. (that's why we use the try... catch... for dynamic_cast)

2). We pass argument by reference (not by value) in the operator argument.
Why must we use void operator= (const Parent &p) rather than void operator= (const Parent p) ?
I tested it, both works, both produce same output.
But in this case, is reference more preferable in the argument, why?

Thanks.
Last edited on
1). Is this approach safer than using dynamic_cast?

No.


dynamic_cast seems a bit indeterministic during run-time.. (if save enough why you put try... catch...)

In this simple case dynamic cast isn't necessary, but suppose you added a data member to child? How do you know if it's safe to access that member for p in your operator=? What if the actual type of p is Parent and it has no such member?
Thanks.
All problems are solved.
I will use the dynamic_cast approach because it is clean and simple.

Thanks everyone !
Last edited on
Hi everyone,

I come across this guideline about casting in C++.

1). Avoid casts whenever practical, especially dynamic_cast in performance-sensitive code.
If a design requires casting, try to develop a cast-free alternative.

2). When casting is necessary, try to hide it inside a function. Clients can then call the function instead of putting casts in their own code.

3). Prefer C++ casts to old-style casts. They are easier to see, and they are more specific about what they do.

Above are extracted from Item 27 (page123) the book:
Title: Effective C++ Third Edition
Author: Scott Meyers
ISBN-13: 978-0-321-33487-9
ISBN-10: 0-321-33487-6


Is this true? If yes, I need to rethink my casting strategy above..
Last edited on
> If a design requires casting, try to develop a cast-free alternative.

Yes.


> Avoid ... especially dynamic_cast in performance-sensitive code.
> Is this true? If yes, I need to rethink my casting strategy above..

A dynamic_cast has a run-time overhead;
Given a pointer to an object of a polymorphic class, a cast to a pointer to another base subobject of the same derived class object can be done using a dynamic_cast. In principle, this operation involves finding the virtual function table, through that finding the most-derived class object of which the object is part, and then using type information associated with that object to determine if the conversion (cast) is allowed, and finally performing any required adjustments of the this pointer. In principle, this checking involves the traversal of a data structure describing the base classes of the most derived class. Thus, the run-time cost of a dynamic_cast may depend on the relative positions in the class hierarchy of the two classes involved.
- Technical report on C++ performance (TR-18015)

http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf

Measure it on your specific implementation; and see if it is affordable for you or not. As far as performance goes, a single measurement is worth more than a thousand opinions.

For most programs, numbers like these (single-level casts) would be more than acceptable:
1000000 dynamic_down_cast took 60 msecs
1000000 dynamic_cross_cast took 80 msecs
1000000 dynamic_down_cast_virtual took 90 msecs
1000000 dynamic_cross_cast_virtual took 100 msecs

http://liveworkspace.org/code/26C7kE$0
Last edited on
Thanks a lot !
Topic archived. No new replies allowed.