Reputation: 20765
Suppose I have a class Foo
whose implementation involves a large object. I want to extend the functionality of Foo
, and so I decide to write a class Bar
which inherits Foo
:
class Foo {
protected:
int bigobject;
public:
Foo() : bigobject(1) { }
int get() { return bigobject; }
void set(int x) { bigobject = x; }
};
class Bar : public Foo {
public:
Bar(Foo& f) { /* ??? */ }
void multiply(int factor) { bigobject *= factor; }
};
As you can see, I've left the constructor blank. This is because what I'd like to do is have the base members of any Bar
object be references to members of an existing Foo
instance. In other words, I want the code:
Foo f;
f.set(5);
std::cout << "f is " << f.get() << std::endl;
Bar b(f);
std::cout << "b is " << b.get() << std::endl;
b.multiply(2);
std::cout << "b is " << b.get() << std::endl;
std::cout << "f is " << f.get() << std::endl;
To result in:
f is 5
b is 5
b is 10
f is 10
So that f
and the base part of b
share the same memory space. This is to avoid copying bigobject
any time I want to instantiate a Bar
. In informal terms, I want a Bar
object b
to be a "view" of a Foo
object f
. Calling any of Foo
's member methods would change f
, but I could also define more methods as part of Bar
which would change f
as well.
Now, I could declare a global "storage" variable as such:
int bigobject_storage = 1;
class Foo {
protected:
int& bigobject;
public:
Foo() : bigobject(bigobject_storage) { }
int get() { return bigobject; }
void set(int x) { bigobject = x; }
};
class Bar : public Foo {
public:
void multiply(int factor) { bigobject *= factor; }
};
To get the functionality I want, but this seems hackish and leaks the implementation. So, is there an idiomatic C++ way to accomplish this?
Upvotes: 0
Views: 132
Reputation: 21
You can achieve what you want using nothing more than OOP concepts.
All you need to do is a pointer to the object, and the polymorphism will do the job.
You don't have yo change nothing in your code, nor you have to give Bar a copy of Bar, all you need to do is call the constructor of the base class (although that is done automatically, in the case of the default constructor), in your derived class.
Something like this:
class Foo {
protected:
int bigobject;
public:
Foo() : bigobject(1) { }
int get() { return bigobject; }
void set(int x) { bigobject = x; }
};
class Bar : public Foo {
public:
Bar() : Foo() { }
void multiply(int factor) { bigobject *= factor; }
};
The trick is in deciding how you want to "view" the object. Because Foo is the base class of Bar, Bar has everything that Foo has. In other words, Bar is Foo plus what you define in Bar. So a Foo pointer pointing to Bar will behave as a Foo object, even if it is pointing to Bar.
The code would be like this:
Bar b();
/*
/* f and b point to the same object.
/* But, to the compiler, f does'nt know anything of b and its methods,
/* because f is a pointer to Foo.
*/
Foo *f = &b;
f->set(5)
std::cout << "f is " << f->get() << std::endl;
std::cout << "b is " << b.get() << std::endl;
b.multiply(2);
std::cout << "b is " << b.get() << std::endl;
std::cout << "f is " << f->get() << std::endl;
To have this result:
f is 5
b is 5
b is 10
f is 10
And even you are saving the memory of one object, because you are using the same object, but viewed as different classes. That is polymorphism.
Upvotes: 0
Reputation: 154045
David Wheeler seems to be responsible for the quote
All problems in computer science can be solved by another level of indirection.
This applies in your case: Have your base class Foo
store a [smart] pointer to the big object in question and share the corresponding pointer across the relevant instances. When default constructing a Foo
object a big object is allocated and pointed to. When copying Foo
objects, the new Foo
object is set to reference the same big object as the original. Note, however, that this means that the Foo
bases behave more like references to each other than values.
In practical terms, you'd probably use something like this:
class Foo {
protected: // I don't really believe in protected data members! Don't use them!
std::shared_ptr<int> bigobject;
public:
Foo(): bigobject(new int()) {}
Foo(Foo const& other): bigobject(other.bigobject()) {}
// ...
};
Bar::Bar(Foo& other): Foo(other) { /*...*/ }
Upvotes: 1