Reputation: 839
I had my doubts since I first saw where it leads, but now that I look at some code I have (medium-ish beginner), it strikes me as not only ugly, but potentially slow?
If I have a struct S
inside a class A
, called with class B
(composition), and I need to do something like this:
struct S { int x[3] {1, 2, 3}; };
S *s;
A(): s {new S} {}
B(A *a) { a->s->x[1] = 4; }
How efficient is this chain: a->s->x[1]
? Is this ugly and unnecessary? A potential drag? If there are even more levels in the chain, is it that much uglier? Should this be avoided? Or, if by any chance none of the previous, is it a better approach than:
S s;
B(A *a): { a->s.x[1] = 4; }
It seems slower like this, since (if I got it right) I have to make a copy of the struct
, rather than working with a pointer to it. I have no idea what to think about this.
Upvotes: 1
Views: 364
Reputation: 36463
is it a better approach
In the case you just showed no, not at all.
First of all, in modern C++ you should avoid raw pointers with ownership which means that you shouldn't use new
, never. Use one of the smart pointers that fit your needs:
std::unique_ptr
for sole ownership.std::shared_ptr
for multiple objects -> same resource.I can't exactly tell you about the performance but direct access through the member s
won't ever be slower than direct access through the member s
that is dereferenced. You should always go for the non-pointer way here.
But take another step back. You don't even need pointers here in the first place. s
should just be an object like in your 2nd example and replace the pointer in B
's constructor for a reference.
I have to make a copy of the struct, rather than working with a pointer to it.
No, no copy will be made.
Upvotes: 2
Reputation: 35154
If you consider a->s->x[1] = 4
as ugly, then it is rather because of the chain than because of the arrows, and a->s.x[1] = 4
is ugly to the same extent. In my opinion, the code exposes S
more than necessary, though there may sometimes exist good reasons for doing so.
Performance is one thing that matters, others are maintainability and adaptability. A chain of member accesses usually supports the principle of information hiding to a lesser extent than designs where such chains are avoided; Involved objects (and therefore the involved code) is tighter coupled than otherwise, and this usually goes on the cost of maintainability (confer, for example, Law of Demeter as a design principle towards better information hiding:
In particular, an object should avoid invoking methods of a member object returned by another method. For many modern object oriented languages that use a dot as field identifier, the law can be stated simply as "use only one dot". That is, the code a.b.Method() breaks the law where a.Method() does not. As an analogy, when one wants a dog to walk, one does not command the dog's legs to walk directly; instead one commands the dog which then commands its own legs.
Suppose, for example, that you change the size of array x
from 3 to 2, then you have to review not only the code of class A
, but potentially that of any other class in your program.
However, if we avoid exposing to much of component S
, class A
could be extended by a member/operator int setSAt(int x, int value)
, which can then also check, for example, array boundaries; changing S
influences only those classes that have S
as component:
B(A *a) { a->setSAt(1,4); }
Upvotes: 0
Reputation: 7220
The real cost of using pointers to objects in many iterations, is not necessarily the dereferencing of the pointer itself, but the potential cost of loading another cache frame into the CPU cache. As long as the pointers points to something within the currently loaded cache frame, the cost is minimal.
Upvotes: 2
Reputation:
Always avoid dynamic allocation with new wherever possible, as it is potentially a very expensive operation, and requires an indirection operation to access the thing you allocated. If you do use it, you should also be using smart pointers, but in your case there is absolutely no reason to do so - just have an instance of S (a value, not a pointer) inside your class.
Upvotes: 0