简体   繁体   中英

Private members vs temporary variables in C++

Suppose you have the following code:

int main(int argc, char** argv) {
    Foo f;
    while (true) {
        f.doSomething();
    }
}

Which of the following two implementations of Foo are preferred?

Solution 1:

class Foo {
    private:
        void doIt(Bar& data);
    public:
        void doSomething() {
            Bar _data;
            doIt(_data);
        }
};

Solution 2:

class Foo {
    private:
        Bar _data;
        void doIt(Bar& data);
    public:
        void doSomething() {
            doIt(_data);
        }
};

In plain english: if I have a class with a method that gets called very often, and this method defines a considerable amount of temporary data (either one object of a complex class, or a large number of simple objects), should I declare this data as private members of the class?

On the one hand, this would save the time spent on constructing, initializing and destructing the data on each call, improving performance. On the other hand, it tramples on the "private member = state of the object" principle, and may make the code harder to understand.

Does the answer depend on the size/complexity of class Bar? What about the number of objects declared? At what point would the benefits outweigh the drawbacks?

First of all it depends on the problem being solved. If you need to persist the values of temporary objects between calls you need a member variable. If you need to reinitialize them on each invokation - use local temporary variables. It a question of the task at hand, not of being right or wrong.

Temporary variables construction and destruction will take some extra time (compared to just persisting a member variable) depending on how complex the temporary variables classes are and what their constructors and destructors have to do. Deciding whether the cost is significant should only be done after profiling, don't try to optimize it "just in case".

From a design point of view, using temporaries is cleaner if that data is not part of the object state, and should be preferred.

Never make design choices on performance grounds before actually profiling the application. You might just discover that you end up with a worse design that is actually not any better than the original design performance wise.

To all the answers that recommend to reuse objects if construction/destruction cost is high, it is important to remark that if you must reuse the object from one invocation to another, in many cases the object must be reset to a valid state between method invocations and that also has a cost. In many such cases, the cost of resetting can be comparable to construction/destruction.

If you do not reset the object state between invocations, the two solutions could yield different results, as in the first call, the argument would be initialized and the state would probably be different between method invocations.

Thread safety has a great impact on this decision also. Auto variables inside a function are created in the stack of each of the threads, and as such are inherently thread safe. Any optimization that pushes those local variable so that it can be reused between different invocations will complicate thread safety and could even end up with a performance penalty due to contention that can worsen the overall performance.

Finally, if you want to keep the object between method invocations I would still not make it a private member of the class (it is not part of the class) but rather an implementation detail (static function variable, global in an unnamed namespace in the compilation unit where doOperation is implemented, member of a PIMPL...[the first 2 sharing the data for all objects, while the latter only for all invocations in the same object]) users of your class do not care about how you solve things (as long as you do it safely, and document that the class is not thread safe).

// foo.h
class Foo {
public:
   void doOperation();
private:
   void doIt( Bar& data );
};

// foo.cpp
void Foo::doOperation()
{
   static Bar reusable_data;
   doIt( reusable_data );
}

// else foo.cpp
namespace {
  Bar reusable_global_data;
}
void Foo::doOperation()
{
   doIt( reusable_global_data );
}

// pimpl foo.h
class Foo {
public:
   void doOperation();
private:
   class impl_t;
   boost::scoped_ptr<impl_t> impl;
};

// foo.cpp
class Foo::impl_t {
private:
   Bar reusable;
public:
   void doIt(); // uses this->reusable instead of argument
};

void Foo::doOperation() {
   impl->doIt();
}

I'd declare _data as temporary variable in most cases. The only drawback is performance, but you'll get way more benefits. You may want to try Prototype pattern if constructing and destructing are really performance killers.

If it is semantically correct to preserve a value of Bar inside Foo, then there is nothing wrong with making it a member - it is then that every Foo has-a bar.

There are multiple scenarios where it might not be correct, eg

  1. if you have multiple threads performing doSomething, would they need all separate Bar instances, or could they accept a single one?
  2. would it be bad if state from one computation carries over to the next computation.

Most of the time, issue 2 is the reason to create local variables: you want to be sure to start from a clean state.

Like a lot of coding answers it depends.

Solution 1 is a lot more thread-safe. So if doSomething were being called by many threads I'd go for Solution 1.

If you're working in a single threaded environment and the cost of creating the Bar object is high, then I'd go for Solution 2.

In a single threaded env and if the cost of creating Bar is low, then I think i'd go for Solution 1.

You have already considered "private member=state of the object" principle, so there is no point in repeating that, however, look at it in another way.

A bunch of methods, say a, b, and c take the data "d" and work on it again and again. No other methods of the class care about this data. In this case, are you sure a, b and c are in the right class?

Would it be better to create another smaller class and delegate, where d can be a member variable? Such abstractions are difficult to think of, but often lead to great code.

Just my 2 cents.

Is that an extremely simplified example? If not, what's wrong with doing it this

void doSomething(Bar data);

int main() {
    while (true) {
        doSomething();
    }
}

way? If doSomething() is a pure algorithm that needs some data ( Bar ) to work with, why would you need to wrap it in a class? A class is for wrapping a state (data) and the ways (member functions) to change it.

If you just need a piece of data then use just that: a piece of data. If you just need an algorithm, then use a function. Only if you need to keep a state (data values) between invocations of several algorithms (functions) working on them, a class might be the right choice.

I admit that the borderlines between these are blurred, but IME they make a good rule of thumb.

If it's really that temporary that costs you the time, then i would say there is nothing wrong with including it into your class as a member. But note that this will possibly make your function thread-unsafe if used without proper synchronization - once again, this depends on the use of _data .

I would, however, mark such a variable as mutable . If you read a class definition with a member being mutable , you can immediately assume that it doesn't account for the value of its parent object.

class Foo {
    private:
        mutable Bar _data;
    private:
        void doIt(Bar& data);
    public:
        void doSomething() {
            doIt(_data);
        }
};

This will also make it possible to use _data as a mutable entity inside a const function - just like you could use it as a mutable entity if it was a local variable inside such a function.

If you want Bar to be initialised only once (due to cost in this case). Then I'd move it to a singleton pattern.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM