Skip to content

Commit 8016acc

Browse files
authored
Address mistakes in inline functions cpp
1 parent c009e1d commit 8016acc

File tree

1 file changed

+69
-37
lines changed

1 file changed

+69
-37
lines changed

docs/cpp/inline-functions-cpp.md

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,38 @@ A function defined in the body of a class declaration is implicitly an inline fu
2020
In the following class declaration, the `Account` constructor is an inline function because it is defined in the body of the class declaration. The member functions `GetBalance`, `Deposit`, and `Withdraw` are specified `inline` in their definitions. The `inline` keyword is optional in the function declarations in the class declaration.
2121

2222
```cpp
23-
// Inline_Member_Functions.cpp
23+
// account.h
2424
class Account
2525
{
2626
public:
27-
Account(double initial_balance) { balance = initial_balance; }
28-
double GetBalance();
29-
double Deposit( double Amount );
30-
double Withdraw( double Amount );
27+
Account(double initial_balance)
28+
{
29+
balance = initial_balance;
30+
}
31+
32+
double GetBalance() const;
33+
double Deposit(double amount);
34+
double Withdraw(double amount);
35+
3136
private:
3237
double balance;
3338
};
3439

35-
inline double Account::GetBalance()
40+
inline double Account::GetBalance() const
3641
{
3742
return balance;
3843
}
3944

40-
inline double Account::Deposit( double Amount )
41-
{
42-
return ( balance += Amount );
43-
}
44-
45-
inline double Account::Withdraw( double Amount )
45+
inline double Account::Deposit(double amount)
4646
{
47-
return ( balance -= Amount );
47+
balance += amount;
48+
return balance;
4849
}
4950

50-
int main()
51+
inline double Account::Withdraw(double amount)
5152
{
53+
balance -= amount;
54+
return balance;
5255
}
5356
```
5457
@@ -90,10 +93,9 @@ Use the [`/Ob`](../build/reference/ob-inline-function-expansion.md) compiler opt
9093
```cpp
9194
// inline_keyword1.cpp
9295
// compile with: /c
93-
inline int max( int a , int b ) {
94-
if( a > b )
95-
return a;
96-
return b;
96+
inline int max(int a, int b)
97+
{
98+
return a < b ? b : a;
9799
}
98100
```
99101

@@ -105,13 +107,14 @@ A class's member functions can be declared inline, either by using the **`inline
105107
// inline_keyword2.cpp
106108
// compile with: /EHsc /c
107109
#include <iostream>
108-
using namespace std;
109110

110-
class MyClass {
111+
class MyClass
112+
{
111113
public:
112-
void print() { cout << i << ' '; } // Implicitly inline
114+
void print() { std::cout << i; } // Implicitly inline
115+
113116
private:
114-
int i;
117+
int i;
115118
};
116119
```
117120

@@ -152,13 +155,15 @@ A `Point` class can be defined as follows:
152155

153156
```cpp
154157
// when_to_use_inline_functions.cpp
158+
// compile with: /c
155159
class Point
156160
{
157161
public:
158-
// Define "accessor" functions as
159-
// reference types.
162+
// Define "accessor" functions
163+
// as reference types.
160164
unsigned& x();
161165
unsigned& y();
166+
162167
private:
163168
unsigned _x;
164169
unsigned _y;
@@ -168,13 +173,11 @@ inline unsigned& Point::x()
168173
{
169174
return _x;
170175
}
176+
171177
inline unsigned& Point::y()
172178
{
173179
return _y;
174180
}
175-
int main()
176-
{
177-
}
178181
```
179182

180183
Assuming coordinate manipulation is a relatively common operation in a client of such a class, specifying the two accessor functions (`x` and `y` in the preceding example) as **`inline`** typically saves the overhead on:
@@ -193,7 +196,9 @@ A macro has some things in common with an `inline` function. But there are impor
193196
```cpp
194197
#include <iostream>
195198

196-
#define mult(a, b) a * b
199+
#define mult1(a, b) a * b
200+
#define mult2(a, b) (a) * (b)
201+
#define mult3(a, b) ((a) * (b))
197202

198203
inline int multiply(int a, int b)
199204
{
@@ -202,40 +207,67 @@ inline int multiply(int a, int b)
202207

203208
int main()
204209
{
205-
std::cout << mult(5 + 5, 5 + 5) << std::endl; // outputs 35
206-
std::cout << multiply(5 + 5, 5 + 5) << std::endl; // outputs 100
210+
std::cout << (48 / mult1(2 + 2, 3 + 3)) << std::endl; // outputs 33
211+
std::cout << (48 / mult2(2 + 2, 3 + 3)) << std::endl; // outputs 72
212+
std::cout << (48 / mult3(2 + 2, 3 + 3)) << std::endl; // outputs 2
213+
std::cout << (48 / multiply(2 + 2, 3 + 3)) << std::endl; // outputs 2
207214

208-
std::cout << mult(2, 2.2) << std::endl>>; // no warning
215+
std::cout << mult3(2, 2.2) << std::endl; // no warning
209216
std::cout << multiply(2, 2.2); // Warning C4244 'argument': conversion from 'double' to 'int', possible loss of data
210217
}
211218
```
212219
213220
```Output
214-
35
215-
100
221+
33
222+
72
223+
2
224+
2
216225
4.4
217226
4
218227
```
219228

220229
Here are some of the differences between the macro and the inline function:
221230

222231
- Macros are always expanded inline. However, an inline function is only inlined when the compiler determines it is the optimal thing to do.
223-
- The macro may result in unexpected behavior. For example, the macro `mult(5+5,5+5)` expands to `5 + 5 * 5 + 5` resulting in 35, whereas the function evaluates `10 * 10`. You could address that by defining the macro as #define `mult(a, b) ((a)*(b))`.
232+
- The macro may result in unexpected behavior, which can lead to subtle bugs. For example, the expression `mult1(2 + 2, 3 + 3)` expands to `2 + 2 * 3 + 3` which evaluates to 11, but the expected result is 24. A seemingly valid fix is to add parentheses around both arguments of the function macro, resulting in `#define mult2(a, b) (a) * (b)`, which will solve the issue at hand but can still cause surprising behavior when part of a larger expression. That was demonstrated in the preceding example, and the problem could be addressed by defining the macro as such `#define mult3(a, b) ((a) * (b))`.
224233
- An inline function is subject to semantic processing by the compiler, whereas the preprocessor expands macros without that same benefit. Macros aren't type-safe, whereas functions are.
225234
- Expressions passed as arguments to inline functions are evaluated once. In some cases, expressions passed as arguments to macros can be evaluated more than once. For example, consider the following:
226235

227236
```cpp
237+
#include <iostream>
238+
228239
#define sqr(a) ((a) * (a))
229240

241+
int increment(int& number)
242+
{
243+
return number++;
244+
}
245+
246+
inline int square(int a)
247+
{
248+
return a * a;
249+
}
250+
230251
int main()
231252
{
232253
int c = 5;
233-
std::cout << sqr(c++) << std::endl; // outputs 25
234-
std::cout << c << std::endl; // outputs 7!
254+
std::cout << sqr(increment(c)) << std::endl; // outputs 30
255+
std::cout << c << std::endl; // outputs 7
256+
257+
c = 5;
258+
std::cout << square(increment(c)) << std::endl; // outputs 25
259+
std::cout << c; // outputs 6
235260
}
236261
```
237262
238-
In this example, the expression `c++` is evaluated twice; once for each occurrence of `a` in the macro expansion. Instead, if `sqr` were an inline function, the expression `c++` would be evaluated only once.
263+
```Output
264+
30
265+
7
266+
25
267+
6
268+
```
269+
270+
In this example, the function `increment` is called twice as the expression `sqr(increment(c))` expands to `((increment(c)) * (increment(c)))`. This caused the second invocation of `increment` to return 6, hence the expression evaluates to 30. Any expression that contains side effects may affect the result when used in a macro, examine the fully expanded macro to check if the behavior is intended. Instead, if the inline function `square` was used, the function `increment` would only be called once and the correct result of 25 will be obtained.
239271

240272
## See also
241273

0 commit comments

Comments
 (0)