Solved

Copying a virtual baseclass

Posted on 2007-11-25
35
267 Views
Last Modified: 2010-04-01
Hi,

I have something like this:

    vector<BaseType*>  Stuff;
    Stuff.push_back(new DerivedType());

Later in my app, I want to make a copy of that object. What's the right way to do it?

    BaseType* pCopy = new BaseType(Stuff[0]);

I'm not quite sure what will happen because Stuff[0] is now represented as a base type even though it was allocated as a derived type. I want to allocate a copy of it as a base type to interact with it via it's base class interface.

Thanks
0
Comment
Question by:DJ_AM_Juicebox
  • 23
  • 7
  • 4
  • +1
35 Comments
 
LVL 55

Expert Comment

by:Jaime Olivares
ID: 20346283
BaseType* pCopy = new BaseType(Stuff[0]);

if you want a copy, this is good approach, as far the Base's copy constructor is well implemented as:

BaseType::BaseType(BaseType &old)
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346299
Of course, if the base class is an abstract class this won't work!
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346302
This won't work...
#include <vector>
 

struct A

{

	virtual void foo() = 0;

};
 

struct B : A

{

	void foo(){}

};
 

int main()

{

	std::vector<A *> v;
 

	v.push_back(new B);

	A const *a2 = new A(*v[0]);
 

	a2.foo();

}

Open in new window

0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346307
I think you need to ask yourself why you want to do this! It is not a particularly safe thing to do as you'll be 'slicing' the type if it was originally created from a derived class. Maybe it would be better if you stated here why you think you need to do this as there is probably a better way!
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346330
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 20346347
You can have a virtual copy method - something like this :

#include <iostream>
 

class Base {

  protected :

    int val;

  public :

    Base(int v) : val(v) { }

    Base(Base &b) : val(b.val) { }

    virtual Base *copy() {

        return new Base(*this);

    }

    virtual void print() {

        std::cout << "Base (" << val << ") !!" << std::endl;

    }

};
 

class Derived : public Base {

  public :

    Derived(int v) : Base(v) { }

    Derived(Derived &d) : Base(d.val) { }

    virtual Derived *copy() {

        return new Derived(*this);

    }

    virtual void print() {

        std::cout << "Derived (" << val << ") !!" << std::endl;

    }

};
 

int main(void) {

  Base b(5);

  Derived d(10);

  

  Base *bp = &b;

  bp->print();

  Base *bp2 = bp->copy();

  bp2->print();

  delete bp2;

  

  bp = &d;

  bp->print();

  bp2 = bp->copy();

  bp2->print();

  delete bp2;

  

  return 0;

}

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 20346348
But it's not very pretty ... so you need a good reason ;)
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346352
>> so you need a good reason
Yes indeed -- It's potentially unsafe if you don't realize you've sliced and still expect polymorphic behavior!
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346512
Providing a constructor and assignment operator in the sub class for the super class is probably the best and safest way to do this...
#include <vector>
 

class A

{

public:

	A(int x): m_x(x){}

	virtual void foo() = 0;
 

	void set_x(int x) { m_x = x; }

	int get_x() const { return m_x; }
 

private:

	int m_x;

};
 

class B : public A

{

public:

	B():A(0){}
 

	// Constructor to construct from A

	B(A const & a): A(a), m_y(0){}
 

	// Assignment operator to assign A

	B & operator=(A const & a)

	{

		if(this != &a)

		{

			A::set_x(a.get_x());

			m_y = 0;

		}
 

		return *this;

	}
 

	void foo(){}
 

private:

	int m_y;

};
 

int main()

{

	std::vector<A *> v;
 

	v.push_back(new B);

	A const *a2 = new B(*v[0]);

}

Open in new window

0
 
LVL 53

Expert Comment

by:Infinity08
ID: 20346533
>>         A const *a2 = new B(*v[0]);

But that means you have to know the type - ie. it's not polymorphic.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346542
Unless you have access to modify the base class to force polymorphic copy via a virtualized method (as you have demonstrated) there is no other way to do this.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346549
NB. The question never stated is had to be polymorphic!
>>Later in my app, I want to make a copy of that object. What's the right way to do it?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 20346551
>> NB. The question never stated is had to be polymorphic!

It just seemed logical ;)
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346560
I'm not saying your solution isn't right, but it assumes

a) You have access to modify the base
b) The solution has to be polymorphic

:-p
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346595
Also, try putting a pure and virtual function in the base, your copy version will fail!
0
 

Author Comment

by:DJ_AM_Juicebox
ID: 20346610
Hmm ok here's why I (think) I need to do it:

class BaseType {
      virtual bool IsMyType(string str) = 0;
      virtual bool ExecuteStuff() = 0;
};

class Apple()
{
      int m_MemberVar;
      bool IsMyType() { return str == "Apple"; }
      bool ExecuteStuff()
      {
          m_MemberVar = 55;
           return true;
      }
};

int main()
{
    vector<BaseType*> Stuff;
    Stuff.push_back(new Apple);
    Stuff.push_back(new Orange);

     RunThread(&Stuff);
     RunThread(&Stuff);
}

void RunThread(vector<BaseType*>)
{
      for (all in vector) {
            if (it->IsMyType()) {
                 it->ExecuteStuff();
            }
     }
}  

I hope the pseudo code is somewhat clear. Each thread can use that vector to check if some data matches one of the allocated types. If it does, I want to call their ExecuteStuff() function, but that will modify member variables, and if that's being done between threads I will get some unexpected results.

So at the moment if I realize that a type matches, I wanted to make a local copy for it to execute its function safely:

    if (it->IsMyType()) {
         BaseType* pTemp = new BaseType(it);
         pTemp->ExecuteStuff();
     }

I know it's kind of ugly, I hope what I'm trying to do makes sense in the sample!
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 20346611
Just a small side-note ... instead of copy, I should have named the method clone ... It's a nicer name :)
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 
LVL 40

Expert Comment

by:evilrix
ID: 20346616
Why don't you just synchronize access to the vector using a Mutex?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 20346623
>> Why don't you just synchronize access to the vector using a Mutex?

That would be easier indeed ...

And from your example, it seems you don't need polymorphic copies, since you know the type of the object the moment you want to copy it ... so, evilrix's approach would work nicely (no need for my virtual clone method) if you still want to copy.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346660
This is how to implement a clone method so that it is (a) polymorphic and (b) will work event with an abstract base class: -
#include <vector>
 

class A

{

public:

	A(int x): m_x(x){}

	virtual void foo() = 0;

 

	void set_x(int x) { m_x = x; }

	int get_x() const { return m_x; }
 

	virtual A * clone() = 0;

 

private:

	int m_x;

};
 

class B : public A

{

public:

	B():A(0){}

 

	// Constructor to construct from A

	B(A const & a): A(a), m_y(0){}

 

	// Assignment operator to assign A

	B & operator=(A const & a)

	{

		if(this != &a)

		{

			A::set_x(a.get_x());

			m_y = 0;

		}

 

		return *this;

	}

 

	A * clone()

	{

		return new B(*this);

	}
 

	void foo(){}

 

private:

	int m_y;

};

 

int main()

{

	std::vector<A *> v;

 

	v.push_back(new B);

	A const *a2 = v[0]->clone();

}

Open in new window

0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346686
And another way that uses static polymorphism to ensure that the clone method on B can return a type B* rather than a type A* (just in case that was a problem)
#include <vector>
 

template <typename subT>

class AT

{

public:

	AT(int x): m_x(x){}

	virtual void foo() = 0;

 

	void set_x(int x) { m_x = x; }

	int get_x() const { return m_x; }
 

	virtual subT * clone() = 0;

 

private:

	int m_x;

};
 

class B;

typedef AT<B> A;
 

class B : public A

{

public:

	B():A(0){}

 

	// Constructor to construct from A

	B(A const & a): A(a), m_y(0){}

 

	// Assignment operator to assign A

	B & operator=(A const & a)

	{

		if(this != &a)

		{

			A::set_x(a.get_x());

			m_y = 0;

		}

 

		return *this;

	}

 

	B * clone()

	{

		return new B(*this);

	}
 

	void foo(){}

 

private:

	int m_y;

};

 

int main()

{

	std::vector<A *> v;

 

	v.push_back(new B);

	A const *a1 = v[0]->clone();

}

Open in new window

0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346697
However, I think the solution to your problem is probably to use this....

http://msdn2.microsoft.com/en-us/library/ms682411.aspx
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20346705
0
 
LVL 40

Accepted Solution

by:
evilrix earned 500 total points
ID: 20346765
Ok, I haven't tested this (it builds :) ) but this is the kind of thing you want to be looking at doing...
#include "windows.h"
 

// Scoped lock for critical section so it's unlocked even if an exception is thrown

class CS_ScopedLock

{

public:

	CS_ScopedLock(CRITICAL_SECTION & cs) :m_cs(cs)

	{

		EnterCriticalSection(&m_cs);

	}

	~CS_ScopedLock()

	{

		LeaveCriticalSection(&m_cs);

	}
 

private:

	CRITICAL_SECTION & m_cs;

};
 

// Base type has instance critical section

struct BaseType {
 

	BaseType(): n(0)

	{

		InitializeCriticalSection(&m_cs);

	}
 

	~BaseType()

	{

		DeleteCriticalSection(&m_cs);

	}
 

	virtual bool ExecuteStuff() = 0;
 

	int n;
 

	CRITICAL_SECTION m_cs;

};
 
 

struct Apple : public BaseType

{

	bool ExecuteStuff()

	{

		// Lock this function against access from another thread

		CS_ScopedLock csl(m_cs);

		int n = 1;

		return true;

		// Automatically unlocked when CS_ScopedLock goes out of scope

	}

};
 
 

struct Orange : public BaseType

{

	bool ExecuteStuff()

	{

		// Lock this function against access from another thread

		CS_ScopedLock csl(m_cs);

		int n = 2;

		return true;

		// Automatically unlocked when CS_ScopedLock goes out of scope

	}

};

Open in new window

0
 

Author Comment

by:DJ_AM_Juicebox
ID: 20346776
gahhhh my head is gonna explode.

Ok let me review this,

Thanks
0
 

Author Comment

by:DJ_AM_Juicebox
ID: 20347880
Ok guys I just read through.

I want the solution to be polymorphic, and I do have access to the base class (which is indeed abstract).

So I guess I can go with Infinity's solution (6th post down?). I don't want to use a critical section because I think it's too much compared to simply copying the object. There may be hundreds of threads simultaneously wanting to use it and I'm afraid even the CS would slow things up.

Thanks
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20347902
>> which is indeed abstract
>> So I guess I can go with Infinity's solution (6th post down?).
I've already pointed out it won't work for abstract -- see the modified version I posted that will!
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20347910
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20347941
>> I don't want to use a critical section because I think it's too much compared to simply copying the object.
You're joking right? Using the critical section has far less over head and is simple -- the overhead of a heap allocation and copy is far greater

>> There may be hundreds of threads simultaneously wanting to use it and I'm afraid even the CS would slow things up
I guarantee a copy each time a thread needs access will be a far more costly exercise. As long as the time to execute is minimal the contention of the critical section should be negligible compared to continuous heap allocations. Heap allocations are very costly performance wise; especially if you plan of doing lots of them continuously! Another problem you have with lots of heap allocations is memory fragmentation. The more the memory fragments the slower it will become as the OS tries to find suitable free blocks in the free list..

I think possibly the problem here is that your original design may not really be suitable for what you are trying to do; however, in so far as your original question goes I believe it has now been thoroughly answered.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20347961
I strongly suggest you read these before you decide to dismiss Criitcal Section in favour of frequent small heap allocations: -

http://msdn2.microsoft.com/en-us/library/ms810466.aspx
http://support.microsoft.com/default.aspx?scid=kb;en-us;Q323635
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20347970
Also, access to the heap is protected by a mutual exclusion locks  to prevent multi-threading issues -- so you'll still encounter exactly the same contention issues of using a Critical Section but with the added overhead of the extra work involved in heap allocations!
0
 

Author Comment

by:DJ_AM_Juicebox
ID: 20348210
Ok I see your points. I guess my only (somewhat) valid concern is from an end user's point of view. I just happen to be desgning the class to work in a multithreaded environment, but it could just as well be used in a single threaded environment - in which case they may be really confused as to why I injected a critical section into the class (without having marked for use most likely in a MT environment). I guess it is just not working the way I expected when I first started because I'm trying to desgn the class to do be safe in any situation.

Thanks for all your answers!
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20348658
Well, is the class is designed to work in an MT application either it needs to protect itself or you need to protect it then you use it. Protecting itself provides good encapsulation. You can always conditionally compile in or out the critical section code to provide ST or MT semantics but that'll probably just make the code more confusing :).
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 20348750
>> >> which is indeed abstract
>> >> So I guess I can go with Infinity's solution (6th post down?).
>> I've already pointed out it won't work for abstract -- see the modified version I posted that will!

To be fair : there was no mention of the base class being abstract when I posted ... What I posted was really just to give you an idea of how you could do it - you still had to adapt it to your specific situation of course.
0
 
LVL 40

Expert Comment

by:evilrix
ID: 20348775
Agreed, but it's kind of a moot point because copying the class wasn't the best way to solve this problem anyway :)
0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

In days of old, returning something by value from a function in C++ was necessarily avoided because it would, invariably, involve one or even two copies of the object being created and potentially costly calls to a copy-constructor and destructor. A…
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now