Templates and virtual methods

S

Stefan Nikolaus

Hello,

I've run into problems with defining a template, which inherits from a
base template and the usage of virtual methods in those. I want to
initialize a member variable depending on which template is created and
I tried to define a virtual method, that's called from the base
template ctor. Here's my example code:


#include <iostream>

using namespace std;

template<typename T>
class A
{
public:
A() { cout << "A()" << endl; c = create(); }
virtual ~A() {}

class C
{
public:
virtual ~C() {}
virtual void foo() { cout << "A::C:foo()" << endl; }
};

virtual C* create() { cout << "A::create()" << endl; return new C;
}
C* c;
};

template<typename T>
class B : public A<T>
{
public:
B() : A<T>() { cout << "B()" << endl; }
virtual ~B() {}

class C : virtual public A<T>::C
{
public:
virtual ~C() {}
virtual void foo() { cout << "B::C:foo()" << endl; }
};

virtual typename A<T>::C* create() { cout << "B::create()" << endl;
return new C; }
};

int main( int /*argc*/, char** /*argv*/ )
{
A<int> a;
a.c->foo();

B<int> b;
b.c->foo();
}


Compiling this with gcc 4.1.0 and running it gives me:
A()
A::create()
A::C:foo()
A()
A::create()
B()
A::C:foo()


I've expected, that B::create() will be called on instantiating B<int>.

Why is this not the case?
How do I achieve what I want, preferably by using virtual methods?

Regards,
Stefan
 
M

Mehturt

Compiling this with gcc 4.1.0 and running it gives me:
A()
A::create()
A::C:foo()
A()
A::create()
B()
A::C:foo()


I've expected, that B::create() will be called on instantiating B<int>.

I don't have the answer for this, but here's a bit simpler example:

#include <iostream>

class Base
{
public:
Base()
{ this->fn(); }

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

class Derived : public Base
{
public:
Derived() { }

void fn()
{ std::cout<<"Derived::fn()"<<std::endl; }
};

int main()
{
Base* b = new Derived;
return 0;
}

I guess somebody will come up with a quotation from the standard on why
this works this way..
 
?

=?ISO-8859-1?Q?Stefan_N=E4we?=

Stefan said:
Hello,

I've run into problems with defining a template, which inherits from a
base template and the usage of virtual methods in those. I want to
initialize a member variable depending on which template is created and
I tried to define a virtual method, that's called from the base
template ctor. Here's my example code:


#include <iostream>

using namespace std;

template<typename T>
class A
{
public:
A() { cout << "A()" << endl; c = create(); }
virtual ~A() {}

class C
{
public:
virtual ~C() {}
virtual void foo() { cout << "A::C:foo()" << endl; }
};

virtual C* create() { cout << "A::create()" << endl; return new C;
}
C* c;
};

template<typename T>
class B : public A<T>
{
public:
B() : A<T>() { cout << "B()" << endl; }
virtual ~B() {}

class C : virtual public A<T>::C
{
public:
virtual ~C() {}
virtual void foo() { cout << "B::C:foo()" << endl; }
};

virtual typename A<T>::C* create() { cout << "B::create()" << endl;
return new C; }
};

int main( int /*argc*/, char** /*argv*/ )
{
A<int> a;
a.c->foo();

B<int> b;
b.c->foo();
}


Compiling this with gcc 4.1.0 and running it gives me:
A()
A::create()
A::C:foo()
A()
A::create()
B()
A::C:foo()


I've expected, that B::create() will be called on instantiating B<int>.

Why is this not the case?
How do I achieve what I want, preferably by using virtual methods?

Read this:
http://www.artima.com/cppsource/nevercall.html

/S
 
J

John Carson

Stefan Nikolaus said:
Hello,

I've run into problems with defining a template, which inherits from a
base template and the usage of virtual methods in those. I want to
initialize a member variable depending on which template is created
and I tried to define a virtual method, that's called from the base
template ctor. Here's my example code:


#include <iostream>

using namespace std;

template<typename T>
class A
{
public:
A() { cout << "A()" << endl; c = create(); }
virtual ~A() {}

class C
{
public:
virtual ~C() {}
virtual void foo() { cout << "A::C:foo()" << endl; }
};

virtual C* create() { cout << "A::create()" << endl; return new C;
}
C* c;
};

template<typename T>
class B : public A<T>
{
public:
B() : A<T>() { cout << "B()" << endl; }
virtual ~B() {}

class C : virtual public A<T>::C
{
public:
virtual ~C() {}
virtual void foo() { cout << "B::C:foo()" << endl; }
};

virtual typename A<T>::C* create() { cout << "B::create()" << endl;
return new C; }
};

int main( int /*argc*/, char** /*argv*/ )
{
A<int> a;
a.c->foo();

B<int> b;
b.c->foo();
}


Compiling this with gcc 4.1.0 and running it gives me:
A()
A::create()
A::C:foo()
A()
A::create()
B()
A::C:foo()


I've expected, that B::create() will be called on instantiating
B<int>.
Why is this not the case?

create() is only called in A's constructor, so A::create() is called. Base
classes are always created before derived classes, so B doesn't exist at the
time A's constructor is called, making the virtual function call mechanism
inoperative.

http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.5

Your code leaks memory, by the way, since the A::C object is never deleted.
How do I achieve what I want, preferably by using virtual methods?

As indicated above, you should not be using virtual methods in a
constructor.

Various approaches are described here

http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.6

As an alterative, you can let the derived class call a constructor in the
base class that does the right initialisation. Try this:

#include <iostream>
#include <memory>
using namespace std;

template<typename T>
class A
{

// constructor to be called by derived class
protected:
// forward declaration
class C;
A(C * pDerivedC) : c(pDerivedC)
{
cout << "A(C * pDerivedC)" << endl;
}
public:
// constructor for an A object by itself
A() : c(create()) // calls A's create
{
cout << "A()" << endl;
}
virtual ~A() {}

class C
{
public:
virtual ~C() {}
virtual void foo() { cout << "A::C:foo()" << endl; }
};

C* create()
{
cout << "A::create()" << endl;
return new C;
}
auto_ptr<C> c;
};

template<typename T>
class B : virtual public A<T>
{
public:
B() : A<T>(create()) // calls B's create
{
cout << "B()" << endl;
}
virtual ~B() {}

class C : public A<T>::C
{
public:
virtual ~C() {}
virtual void foo() { cout << "B::C:foo()" << endl; }
};
C* create()
{
cout << "B::create()" << endl;
return new C;
}
};

int main( int /*argc*/, char** /*argv*/ )
{
A<int> a;
a.c->foo();
B<int> b;
b.c->foo();
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,059
Latest member
cryptoseoagencies

Latest Threads

Top