Reputation: 11
Right now, I think, I must make an important decision in my program. The task is the following:
I have single class base
which will be inherited by multiple derived classes. Derived classes must have shared access to same instances of base
members. Also, I want to access those "shared" Base
s members from outside: from a 3rd class Foo
, which contains all derived classes. With my current experience I came up with two solutions:
structs
, int
s and putting everything in constructors for simplicity.struct Base
{
std::shared_ptr<int> m_1 = nullptr;
std::shared_ptr<int> m_2 = nullptr;
};
struct Derived_1 : Base
{
// using Base members and doing other stuff
};
struct Derived_2 : Base
{
// using Base members and doing other stuff
};
struct Foo
{
std::shared_ptr<int> m_1 = std::make_shared<int>();
std::shared_ptr<int> m_2 = std::make_shared<int>();
Derived_1 _d1;
Derived_2 _d2;
Foo()
{
_d1.m_1 = this->m_1;
_d1.m_2 = this->m_2;
_d2.m_1 = this->m_1;
_d2.m_2 = this->m_2;
}
};
This I have access to the same m_1
and m_2
from Foo
and also from derived classes.
static
keyword:struct Base
{
static std::shared_ptr<int> m_1;
static std::shared_ptr<int> m_2;
};
// providing definitions for Base::m_1 and Base::m_2 ...
// Derived classes stay the same as in first example.
class Foo
{
std::shared_ptr<int> m_1;
std::shared_ptr<int> m_2;
Foo()
{
this->m_1 = Base::m_1;
this->m_2 = Base::m_2;
}
};
In both cases I achieve same result (do I really?). Are there some advantages for one approach over the other? What are hidden drawbacks for them? What is more common approach and why? I am curious about other approaches. I am using c++17 under VS17.
Upvotes: 0
Views: 277
Reputation: 238381
In both cases I achieve same result (do I really?).
Well, no. If you use static member, then the member is not associated with any instance, and there is exactly one shared Base::m_1
and one Base::m_2
respectively in the program. With a non-static member, every instance of Base
have their own m_1
and m_2
contained within them.
Are there some advantages for one approach over the other?
One has the advantage that there is only one member in the program, and the other has the advantage that there is one member for each instance.
What is more common approach and why?
Non-static members are typically more useful. Static storage is global state, which is problematic and is discouraged.
It is unclear why Foo
has the members m_1
and m2
, which appear to point to the same object as members point to. Why not simply use those pointers and get rid of the pointers in Foo
?
struct Foo
{
Derived_1 _d1{std::make_shared<int>(), std::make_shared<int>()};
Derived_2 _d2{_d1.m_1, _d1.m_2};
};
Sharing the bases of the derived objects is also possible which would achieve having only a single copy of the shared pointers per instance of Foo
. But it is only possible if the derived instances are themselves bases of a single derived class. In such case, the sharing of the base is achieved with virtual inheritance.
struct Derived_1 : virtual Base {};
struct Derived_2 : virtual Base {};
struct Derived : Derived_1, Derived_2 {
Derived(std::shared_ptr<int> m_1, std::shared_ptr<int> m_2): Base{m_1, m_2} {}
};
struct Foo
{
Derived _d{std::make_shared<int>(), std::make_shared<int>()};
};
Upvotes: 1
Reputation: 122595
They are not the same. Consider this:
int main() {
Foo f1;
Foo f2;
*(f1.m_1) = 5;
*(f2.m_1) = 42;
std::cout << *(f1.m_1);
}
For the first version without static
it will output: 5
, because each Foo
isntance uses its own Base
subobject. The second variant has some errors:
#include <memory>
#include <iostream>
struct Base
{
static std::shared_ptr<int> m_1;
static std::shared_ptr<int> m_2;
};
// defintions were missing
std::shared_ptr<int> Base::m_1 = std::make_shared<int>();
std::shared_ptr<int> Base::m_2 = std::make_shared<int>();
// Foo didnt inherit from Base and had a private constructor
struct Foo : Base
{
std::shared_ptr<int> m_1;
std::shared_ptr<int> m_2;
Foo()
{
this->m_1 = Base::m_1;
this->m_2 = Base::m_2;
}
};
After fixing that, the above main
prints 42
, because there is only one Base::m_1
and only one Base::m_2
.
Upvotes: 0