1

I have a class A with child class B, and want to make a template function to call virtual functions of A and B:

#include <stdio.h>
class A{
public:
    virtual void test(){
        printf("A\n");
    }
};
class B:public A{
public:
    virtual void test(){
        printf("B\n");
    }
};

template<class T>
void f(T t){
    t.test();
}
int main(){
    A* a=new B();
    f<A>(*a);
    return 0;
};

it prints A only and seems does not override test(), but when I change

void f(T t) 

to

void f(T& t) 

like that:

#include <stdio.h>
class A{
public:
    virtual void test(){
        printf("A\n");
    }
};
class B:public A{
public:
    virtual void test(){
        printf("B\n");
    }
};

template<class T>
void f(T& t){
    t.test();
}
int main(){
    A* a=new B();
    f<A>(*a);
    return 0;
};

,it prints B, why would that happen?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
ggrr
  • 7,737
  • 5
  • 31
  • 53

4 Answers4

1

When you use

template<class T>
void f(T t){ ... }

your code suffers from object slicing. You construct a B but only the A part of the object gets passed to f.

That does not happen when you use

template<class T>
void f(T& t){ ... }
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

Over-riding is achieved by using Pointers or References. Please do read a good article regarding polymorphism. Happy coding.

Ali Kazmi
  • 1,460
  • 9
  • 22
0

When you call f(T t), c++ actually new an object of T by constructor T(T& t). Then the reference of this object is passed into function.

You call write code to prove it

class A {
public:
    int x;
    A():x(6){
    }
    A(A& a) {
        x = 2;
    }
    virtual void test() {
        printf("%d\n", x);
    }
};
class B : public A {
public:
    virtual void test() {
        printf("%d\n", x);
    }
};

void fun(A a)
{
    a.test();
}

void f(A& a)
{
    a.test();
}
int main(void)
{
    A* a = new A();
    A* b = new B();
    A* c = new A(*b);
    fun(*a);
    fun(*b);
    f(*a);
    f(*b);
    f(*c);
}

the output is 2 2 6 6 2

no68
  • 143
  • 5
0

How you have your class hierarchy defined is that class B is derived from class A. So when class B's constructor is called it must call A's constructor first before it can be constructed. Both A & B have the same exact virtual function or definition called test().

With your first implementation of f() your template will deduce the parameter type while it is being compiled. It is looking for a class type where in main when you invoke the template function you are telling this template function to expect a type of class A. This will then use A::test() to call the test function. In your main function before you invoke f() you are creating a pointer of type class A dynamically and placing it in the heap, but you are using B's constructor which is a derived type of A. This will use B's constructor to call A's constructor. Your template function is expecting type A so it is invoking a.test() or A::test() in your code.

In your second declaration or definition of f() your classes are defined the same and behave the exact same way. What is different this time is your template function that will be resolved during compilation to deduce its parameter's type. In this version of the function it is expecting the address of class type A since now that it is expecting a memory address as opposed to the actual variable itself, this time when you instantiate or invoke the function f() where it expects a parameter of type T& it is now using the referencing capabilities of c++ and it now invokes b.test().

If you want to see why this is happening use your first declaration of f() and set a break point where you have A* a = new B(); and step into the code line by line, don't step over, but into. Then do the same exact process with your second version of f() and you will see what is happening.

This isn't because the class is or isn't overriding the virtual function; this is due to how template functions work.

Now my question is why would you want to create a pointer for a base type and setting new memory for it by calling its derived type's constructor.

Normally with polymorphism and abstract classes you would usually create a type of derived but you may have a container that holds pointers to the base class of derived where you would normally cast them dynamically. For example let us say that you have a base class of Automobile which is abstract; meaning you can not create this class directly because it's constructor is protected where only derived types can have access to it. Now a derived type might be Car, Van, Truck, SUV, Jeep, MotorCycle and in some other class or function you may have a vector<shared_ptr<Automobile>> stored. So this way you can push a smart pointer of a truck, a car, and a van, all into the same container by dynamically casting these constructed objects pointers to their Base type Automobile since they all publicly inherit from them! However when working with abstract types special care needs to be used.

Check out this little program to see how polymorphism works (there are no abstract types here)

#include <conio.h>
#include <string>
#include <iostream>
#include <vector>
#include <memory>

class Base {
public:
    Base() {}
    virtual ~Base(){}

    virtual void test() const { std::cout << "Base" << std::endl; } 
};

class DerivedA : public Base {
public:
    DerivedA() : Base() {}
    virtual ~DerivedA() {}

    virtual void test() const override { std::cout << "DerivedA" << std::endl; }
};

class DerivedB : public Base {
public:
    DerivedB() : Base() {}
    virtual ~DerivedB() {}

    virtual void test() const override { std::cout << "DerivedB" << std::endl; }
 };

template<class T>
void f( T t ) {
    t.test();
}

template<class T>
void g( T& t ) {
    t.test();
}

int main() {
    DerivedA* a = new DerivedA();
    //f<DerivedA>( *a );
    //g<DerivedA>( *a );

    DerivedB* b = new DerivedB();
    //f<DerivedB>( *b );
    //g<DerivedB>( *b );    

    std::vector<Base*> vBases;
    vBases.push_back( a );
    vBases.push_back( b );

    for ( unsigned i = 0; i < vBases.size(); ++i ) {
        if ( i == 0 ) {
            std::cout << "First Iteration: i = " << i << std::endl;
        } else if ( i == 1 ) {
            std::cout << "Second Iteration: i = " << i << std::endl;
        }

        f<DerivedA>( *dynamic_cast<DerivedA*>( vBases[i] ) );
        f<DerivedB>( *dynamic_cast<DerivedB*>( vBases[i] ) );

        std::cout << std::endl;

        g<DerivedA>( *static_cast<DerivedA*>( vBases[i] ) );
        g<DerivedB>( *static_cast<DerivedB*>( vBases[i] ) );

        std::cout << std::endl;
    }

    delete a;  // You Forgot To Delete Your Dynamic Pointers - Memory Leak!
    delete b;

    std::cout << "Press any key to quit" << std::endl;
    _getch();
    return 0;
}

Output

DerivedA
DerivedB

DerivedA
DerivedA

DerivedA
DerivedB

DerivedB
DerivedB

Maybe this will help you to understand what is happening with your two different template types by using a dynamic_cast<> for f<>() your first version of your template function and using static_cast<> for g<>() for your second version of your template function that takes a reference instead of a stack copy of a variable.

If you remember there are two elements in this vector the first being a DerivedA* and the second being a DerivedB* both which are of a type Base and are stored as a Base* in the vector. The first 4 lines of out put is work that is done on the first element of our vector only! The last 4 lines of out put is work that is done on the second element of our vector only!

Our first stored element at index 0 is of a DerivedA type stored as a Base* and the first call to f<>() we dynamically cast it to a DerivedA* type and we then dereference the pointer. The second call to f<>() we do the same thing except we dynamically cast it to a DerivedB* type and deference it. So here this first stored object is invoking DerivedA::test() then it is invoking DerivedB::test() by using the dynamic cast.

The next two lines are still working on the same element which is our DerivedA* stored as a Base*at index 0 in our vector. This time we are now using g<>() your second method of your function template, and instead of using a dynamic_cast<> we are now using a static_cast<> since g<>() is expecting a reference to an object and not a stack variable itself. If you notice this time around nothing gets casted from one type to another and our function template is always invoking DerivedA::test() even though on the second call to this method we are telling it to cast it to a <DerivedB> type.

For the next 4 lines of output we are now working with our 2nd stored object in the vector at index 1 but this time our saved object is of a DerivedB type stored as a Base*. On the next two lines we have the same output as in the first iteration. This time DerivedB is being casted to a DerivedA for the first call to f<>() and remains its own type for the second call to f<>() and for the final two lines as you can see the stored object at index 1 that is of a DerivedB type stored as a Base* is not being changed or casted to a DerivedA type on the first call of g<>().

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59