Reputation: 866
I'd like to call the class Foo
which has the abstract class Base
in its ctor. I'd like to be able to call Foo
from Derived
which is derived from Base
and use Derived
's overriding methods rather than Base
's.
I'm only able to do this by using a raw pointer as indicated. Is there any way to do this without raw pointers? I tried std::shared_ptr
but the compiler complains about abstract classes. Or perhaps is there a better way?
#include <iostream>
class Base {
public:
Base() {
std::cout << "Hello from Base." << std::endl;
}
virtual void show() const = 0;
};
class Foo {
public:
explicit Foo(const Base *s) { // can I avoid this raw pointer?
std::cout << "Hello from Foo." << std::endl;
s->show();
}
Base *s;
};
class Derived : public Base {
public:
Derived() : Base() {
std::cout << "Hello from Derived." << std::endl;
Foo(this);
}
void show() const override {
std::cout << "Hi, I'm Derived::show()." << std::endl;
}
};
int main() {
Derived();
return EXIT_SUCCESS;
}
which produces the following output:
Hello from Base.
Hello from Derived.
Hello from Foo.
Hi, I'm Derived::show().
Upvotes: 3
Views: 113
Reputation: 1974
The code can be rewritten with const reference to Base
as
#include <iostream>
class Base {
public:
Base() {
std::cout << "Hello from Base." << std::endl;
}
virtual void show() const = 0;
};
class Foo {
public:
explicit Foo(const Base& b) : s(b) { // member initialization list to set s
std::cout << "Hello from Foo." << std::endl;
s.show();
}
const Base& s;
};
class Derived : public Base {
public:
Derived() : Base() {
std::cout << "Hello from Derived." << std::endl;
Foo(*this); // the parameter would be the object itself *this, instead of a pointer this
}
void show() const override {
std::cout << "Hi, I'm Derived::show()." << std::endl;
}
};
int main() {
Derived();
return EXIT_SUCCESS;
}
A reference should be initialized in a member initializer list of the constructor.
When using a raw pointer or a reference 'Foo' should not own 'Base', i.e. Foo
is not responsible for destroying Base
and the lifetime of Base
should be guaranteed by the owner of Foo
.
You have to make sure that Base
(=Derived
in this case) outlives Foo
. That is guaranteed, if the Foo
object is owned by Derived
, e.g. as member or local variable. Then before Base
=Derived
is destroyed, Foo
is destroyed.
You can use normal references instead of const references, but then the same for the constructor parameter as well as the member variable.
A raw pointer (in comparison to a reference) is idiomatic in cases,
nullptr
instead of a valid object orThe first case could be handled by std::optional
instead, the second one with assignment of a lightweight object ('view'), which basically encapsulates a pointer or a reference.
So very few cases (e.g. low-level code, data-structures or for compatibility with C) are left, where raw pointers would still be used in modern C++. And even in those cases, having a wrapper object, which just stores a reference as member variable, would have the same performance (and in practice also the same memory layout) as raw pointers, but are much more clean and safe to use.
In some cases, you would prefer a raw pointer to std::optional
for performance reasons, when execution speed or memory size really matters. As alternative, a reference to nullptr
is not allowed in C++.
Upvotes: 3