1

I have a confusion about C++ function/method design as below:

1.

class ArithmeticCalculation
{ 
private:
    float num1_;
    float num2_;
    float sum_;

    void addTwoNumbers();
};

2.

class ArithmeticCalculation
{ 
private:
    float addTwoNumbers(float num1, float num2);
};

In 1., one can basically declare a class variable and the void addTwoNumbers() will just implement it and assign to the class variable (sum_). I found using 1. is cleaner but using 2. looks like it more intuitive for function use.

Which one is actually best option considering the function/method is not restricted to only this basic addition functionality -- I mean in general how to decide to use with return or simply void?

alfa_80
  • 427
  • 1
  • 5
  • 21
  • 1
    #1 defines a function that takes no args and return nothing. Not a class variable. SHow sample code of how you think it would be used – pm100 Nov 21 '17 at 18:41
  • once you consider how to call those two you will realize that they arent really alternatives. Not everything has to be inside a class so 1) is only an option if you are willing to write tons of boilerplate – 463035818_is_not_an_ai Nov 21 '17 at 18:41
  • @pm100: I mean the void addTwoNumber() is a member function in a class. Both of them are of class member functions. – alfa_80 Nov 21 '17 at 18:44
  • they are indeed, but you seems to think that it does something clever, it doesnt, its a function that takes no args and returns nothing. – pm100 Nov 21 '17 at 18:54
  • I've edited to show my intent in a class. – alfa_80 Nov 21 '17 at 18:59
  • Check out this thread. It has some useful tips. https://stackoverflow.com/questions/8596461/in-c-what-is-the-difference-between-a-method-and-a-function – Larry G. Kanode Nov 21 '17 at 19:01

3 Answers3

5

The major difference between the two functions is that the second one is stateless*, while the first one has a state. Other things being equal, stateless approach is preferred, because it gives the users of your class more flexibility at utilizing your class in their systems. For example, stateless functions are re-entrant, while functions that rely on state may require the code that uses them to take additional measures that prevent incorrect use.

Re-entrancy alone is a big reason to prefer stateless functions whenever possible. However, there are situations when keeping state becomes more economical - for example, when you are using Builder Design Pattern.

Another important advantage of keeping your functions stateless whenever it is possible is that the call sequence becomes more readable. A call of a method that relies on the state consists of these parts:

  • Set up the object before the call
  • Make the call
  • Harvest the result of the call (optional)

Human readers of your code will have much easier time reading the call that uses a function invocation with parameter passing than the three-part setup-call-get result sequence.

There are situations when you have to have state, for example, when you want to defer the action. In this case the parameters are supplied by one part of the code, while the computation is initiated by some other part of the code. In terms of your example, one function would call set_num1 and set_num2, while another function would call addTwoNumbers at some later time. In situations like this you could save the parameters on the object itself, or create a separate object with deferred parameters.

* This is only an assumption based on the signature of your member function. Your second function gets all the data that it needs as parameters, and returns the value to the caller; Obviously, implementations may choose to add some state, e.g. by saving the last result, but that is uncommon for addTwoNumbers functions, so I assume that your code does not do it.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Why is the second stateless? Doesnt that depend on the implementation? He could multiply some class member in it without telling it in the methods name... there is no static modifier – Patrick Artner Nov 21 '17 at 18:51
  • @dasblinkenlight: I got your point. But, what if a function that would need to store in a container like std::vector or std::pair, would it be expensive to return it -- instead of putting it as a class member variable? – alfa_80 Nov 21 '17 at 19:08
  • 2
    @alfa_80 In theory, vectors returned from functions need to be copied, but C++ is allowed to aggressively optimize out the copying. For example, the call could allocate the result vector in the space provided by the caller. In cases like this I would start with the most natural and readable approach, and make modifications only after profiling the application and determining that returning a large container is indeed a major bottleneck. – Sergey Kalinichenko Nov 21 '17 at 19:13
  • @dasblinkenlight: If I got correctly, so you mean, it's still okay even with returning a container (be it passing by value or reference), unless obvious performance overhead can be experienced? – alfa_80 Nov 21 '17 at 19:24
  • 1
    @alfa_80 Yes. For more info on C++ optimizing out the copying, see [this link](http://en.cppreference.com/w/cpp/language/copy_elision). – Sergey Kalinichenko Nov 21 '17 at 19:28
  • @dasblinkenlight: Thanks a lot! – alfa_80 Nov 21 '17 at 19:31
3

The first function doesn't really make a lot of sense. What numbers? Where does the result go? The name doesn't describe the expected side-effects, nor the origin of the numbers in question.

The second function makes it abundantly clear what's going on, where the result is, and how that function might be used.

Your functions should strive to communicate their intent based on the function signature. If that's not sufficient you'll need to add comments or documentation, but no amount of commenting or documentation can pave over a misleading or confusing signature.

Think about what your function's responsibility is as well as whatever expectations it has when naming things. For example:

void whatever(const int);

What does that function do? Could you even guess without looking at code or documentation?

Compare with the same function given a much more meaningful name:

void detonateReactor(const int countdownTimeInSeconds);

It seems pretty clear what that does now, as well as what side-effects it will have.

tadman
  • 208,517
  • 23
  • 234
  • 262
0

You probably had in mind something like this for the first option:

struct Adder {
    float sum;
    float a;
    float b;
    void addNumbers(){ sum = a+b; }
};

that would be used like this:

Adder adder;
adder.a = 1.0;
adder.b = 2.0;
adder.addNumbers();
std::cout << adder.sum << "\n";

There is no single good argument to do this when you actually wanted this:

float addTwoNumbers(float a,float b) { return a+b; }

std::cout << addTwoNumbers(1.0,2.0) << "\n";

Not everything has to be inside a class. Actually not everything should be inside a class (C++ isnt Java). If you need a function that adds two numbers then write a function that adds two numbers and dont overthink it.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185