Reputation: 4539
I'm a bit confused. If you search about the usefulness of (smart) pointers you get very different opinions. Almost everybody agrees that one should use smart pointers over normal pointers, but there are also many opinions that you should not use pointers at all in modern C++.
Lets consider this abstract situation (Which is similar to my problem): You have one class "Clothes" which has a member of type "Hat"
class Hat {
public:
enum class HatType{
sombrero,
sun hat,
helmet,
beanie,
cowboy_hat
};
// some functions
private:
HatType type;
// some members
}
class Clothes {
public:
// some functions
private:
Hat currentHat;
// some other members of other types
}
Is there any Difference in run time if I would change Hat
to Hat*
(or a unique_ptr<Hat>
?).
(In many functions of Clothes
you would need to call something from Hat
)
The reason I ask is because there are many different types of hats.
Like sombreros, sun hats, helmets, beanies and cowboy hats. Right now my class hat has an enumerator which stores the hat type.
The hat type is only relevant in one specific function of Hat
but this is the most used function.
Right now I use a simple switch case
in this specific function and based on the hat type the function is evaluated a bit differently. This worked fine but I think it would be smarter to simply make an own class for every different hat type which inherit from one single main Hat
class and override the one function.
To do so I think I would have to change the member currentHat
in Clothes
to any type of pointer. I researched if this would have any negative effects on my performance (I thought about maybe because the location of my currentHat
object and my Clothes
object in memory can be wide apart, but I have no Idea if this would happen and if this would have any negative effects with modern compilers).
During my research I often read that one should avoid pointers, this made me thinking... some of the information I found was also very old and I do not know if this is outdated. Is there any better way to do this?
Has anyone any experience with this kind of problem? Would be good to get some feedback before I spend a lot of time changing my whole project...
Side note: I took Clothes
and Hats
only as an examples. In my real application I have different types and I am creating many million of objects from type Clothes
and I have to call functions of Clothes
which will call functions of Hat
many many million times, that is why I'm concerned with run time.
Edit: Maybe it is also worth noting that I have entirely avoided pointers in my application so far, mostly because I read in some books that one should try to avoid pointers :-)
EDIT2: Thank you for all your answers. Please note that part of my question was not how to do it (although the answers were very useful in that depart) but rather if the performance will suffer from this.
Upvotes: 2
Views: 188
Reputation: 75785
Ok, let's break down the pointer debacle:
new
/delete
in C++There really isn't any exception to this rule. Except if you are writing a library/framework. Or some fancy stuff that requires placement new. But in user code new
/delete
should be 100% absent.
This brings us to my next rule:
This you will find often on the net as the "Don't use raw pointers" advice/rule. But in my view the problem is not with raw pointers, but with raw pointers that own an object.
For ownership use smart pointers (unique_ptr
, shared_ptr
). If a pointer is not an owner of the object then it's ok if you use raw pointers. For instance in a tree like structure you can have unique_ptr
to the children and a raw pointer to the parent.
You can (arguably) also use a pointer to denote an optional value via nullptr
.
(Well... there are other ways, like type erasure but I won't go there in my post) If you want polymorphism then you can't use a value. If you can use reference then do, but most of the time you can't. Which often means you have to use unique_ptr
.
Upvotes: 5
Reputation: 63144
Here's a breakdown of your three suggested implementations, and some more:
Hat currentHat;
Clothes
uniquely owns the Hat
std::unique_ptr<Hat> currentHat;
Clothes
still uniquely owns the Hat
Hat
Hat *currentHat;
Clothes
does not own the Hat
Hat
outlives the Clothes
std::shared_ptr<Hat> currentHat;
Clothes
shares ownership of the Hat
with zero or more other Clothes
std::unique_ptr
otherwiseHat ¤tHat;
Upvotes: 3
Reputation: 25623
As you need different Heads, you have basically two options:
1) Using a (smart) pointer, this is well known and easy
2) Using std::variant
This is a vary different approach! No longer need of a base class and ( pure ) virtual methods. Instead of this using the std::visit
to access the current object in the tagged union. But this comes with a cost: Dispatching in std::visit can be expensive if the compiler did not hardly optimize the call table inside. But if it can, it is simply taking the tag from the variant and use it as a index to the overloaded function from your visit call, here given as a generic lambda. That is not as fast as indirect call via vtable pointer, but not more than a view instructions more ( if it is optimized!).
As always: How you want t go is a matter of taste.
Example:
class Head1
{
public:
void Print() { std::cout << "Head1" << std::endl; }
};
class Head2
{
public:
void Print() { std::cout << "Head2" << std::endl; }
};
class Clothes
{
std::variant<Head1,Head2> currentHead;
public:
Clothes()
{
currentHead = Head1();
}
void Do() { std::visit( [](auto& head){ head.Print();}, currentHead); }
void SetHead( int id )
{
switch( id )
{
case 0:
currentHead= Head1();
break;
case 1:
currentHead= Head2();
break;
default:
std::cerr << "Wrong id for SetHead" << std::endl;
}
}
};
int main()
{
Clothes cl;
cl.Do();
cl.SetHead(1);
cl.Do();
cl.SetHead(0);
cl.Do();
}
Upvotes: 1