Re: Run-time creation of an object of a dynamically determined class
Hi Kodi,
On 11 Nov, 23:55, Kodi Arfer <withh...@domain.tld> wrote:
Suppose I've got a base-class pointer which is pointing to an object
that belongs to any of a number of derived classes. (Please excuse my
brace style from Saturn.)
struct Barney
{Barney() {};
virtual void greeting() {cout << "Barney says hello.\n";};};
struct Dave : public Barney
{Dave() {};
virtual void greeting() {cout << "Dave says hello.\n";};};
struct Dan : public Barney
{Dan() {};
virtual void greeting() {cout << "Dan says hello.\n";};};
void funkshun()
{Barney *bp1 = &ADaveOrADan;
Barney *bp2;
...}
[...] one workaround occurs to me. I could have Barney
define a virtual "newobj" method, which would be redefined in each
derived class to simply call "new [derived class]". But having each
and every derived class refer to itself in the third person (so to
speak) in that way seems inelegant.
Even though it is a bit of a kludge, this is the usual way to do it.
Often the method is called clone(), or something similar.
As it happens, just this weekend I've been working on a smart pointer
to work around this problem.
It relies on the copy constructor, so you don't need to add warts such
as clone() or newobj() or whatever. You must make sure, however, that
when you construct a value_ptr<> you pass in an object whose static
type is equal to its dynamic type. A quick example:
Dan dan;
value_ptr<Barney> p(dan);
// Copying p will now make a "deep copy" of the object it refers
to:
value_ptr<Barney> p2(p);
p2->greeting(); // "Dan says hello.\n"
// the value_ptr constructor checks for slicing:
Dave dave;
Barney &b = dave;
try
{
value_ptr<Barney> p3(b);
}
catch(const object_sliced &ex)
{
// b's static type is not equal to its dynamic type i.e.
// invoking the copy constructor would slice the object,
// so we end up in here
std::cerr << ex.what() << '\n';
}
So, using this, you might define a factory function such as:
value_ptr<Barney> make(const std::string &name)
{
if (name == "Dan")
{
Dan d;
return value_ptr<Barney>(d);
}
if (name == "Dave")
{
Dave d;
return value_ptr<Barney>(d);
}
if (name == "Barney")
{
Barney b;
return value_ptr<Barney>(b);
}
else
{
throw std::runtime_error("I don't know that guy");
}
}
This way, you don't lose any type information and the copy constructor
will be used forever more. But it might be the case that by the time
you want to make a value_ptr<>, you've lost some of the type
information needed. In this case, you'll still need a cloneable
hierarchy. value_ptr will be able to make use of this.
So let's pretend, now, that your hierarchy really does have a newobj()
to do the cloning.
If you define a free function called clone(), value_ptr will pick it
up and use it instead of the copy constructor:
Barney *clone(const Barney &b)
{
return b.newobj();
}
Dave d;
Barney &b = d;
value_ptr<Barney> p(b); // fine, now. clone() is used. No slicing
occurs
p->greeting(); // "Dave says hello.\n"
You can grab the code here:
http://www.mr-edd.co.uk/files/guff/value_ptr.hpp
Just #include the header and off you go. I've tested it so far on
MSVC8, GCC 3.4.5 and Borland bcc32 5.82
It'll probably make its way on to a 'proper' page of my site by the
end of the week. I'm still tweaking the names of things, but the
functionality is basically there.
Incidentally, I started thinking about this when code with a similar
intent (but entirely different implementation) was posted on the boost
developer's mailing list. You might want to check out the
alternatives:
http://lists.boost.org/Archives/boost/2007/09/126982.php
Hope that helps! Sorry if this post sounded a bit like an advert :)
Edd
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]