Skip to content

Commit

Permalink
docs(const): 📝 add en-us version of const in c++
Browse files Browse the repository at this point in the history
  • Loading branch information
Timothy-Liuxf committed May 22, 2024
1 parent 24219f1 commit edc076d
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 6 deletions.
78 changes: 78 additions & 0 deletions blogs/en-US/c_cpp/why-const-is-essential-in-cpp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Why `const` is essential in C++

> Copyright (C) 2024 Timothy Liu
>
> [Creative Commons - Attribution-ShareAlike 4.0 International - CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en) License
## Preface

Please note that this article is written to supplement a programming course for a certain major at a certain university, so the content may not be very in-depth.

## What will happen if `const` is missing?

In some programming lessons at some universities, there's usually some code like this:

```c++
class Complex
{
public:
Complex(double r, double i) : real(r), imag(i) {}
double getReal() { return real; }
double getImag() { return imag; }
void setReal(double newReal) { real = newReal; }
void setImag(double newImag) { imag = newImag; }
private:
double real;
double imag;
};
```

For beginners, it seems that the code is okay since it can be compiled successfully. However, it has many problems, in fact.

First, the most obvious problem is that if we want to define a const object with type `Complex` and don't want it to be modified, we can write as below:

```cpp
const Complex zero(0.0, 0.0);
// cout << zero.getReal() << endl; // Compile error!
```
The code at Line 2 is incorrect because non-const member functions cannot be called on a const object. Therefore, we should define the member function as a const member function (`double getReal() const`).
Someone would say that they don't want to define const objects so that they don't need to add the `const` keyword, right? Then let's add a function to calculate the modulus of a complex number:
```c++
#include <cmath>
double cabs(Complex& z)
{
return std::sqrt(z.getReal() * z.getReal() + z.getImag() * z.getImag());
}
```

And if one day we call this function as: `cabs(Complex(0.0, 0.0))` and compile it, the compiler will give an error! Very 'amazing', well (but it seems that VS2008 will not give a compilation error, so let's ignore VS2008). But why? Note that what we called 'reference' typically can only be initialized with an [lvalue expression](./lvalue-and-rvalue.md) (after the rvalue reference is introduced into C++11, the original reference is also called 'lvalue reference'), but `Complex(0.0, 0.0)` is an rvalue expression, thus cannot be referenced by lvalue references. Then how should we change the code? One approach is to change the parameter to `Complex z`. However, for some types, copying their objects may cause much overhead, or the copy constructor cannot be accessed or [is deleted](https://en.cppreference.com/w/cpp/language/function#Deleted_functions), so the parameter must be a reference. Then we can use `const` to solve this problem. [In C++, lvalue references to const-qualified (but not volatile-qualified) types can be initialized with both lvalue expressions and rvalue expressions.](https://en.cppreference.com/w/cpp/language/reference_initialization) So the function can be rewritten as below:

```c++
double cabs(const Complex& z)
{
return std::sqrt(z.getReal() * z.getReal() + z.getImag() * z.getImag());
}
```
And there's another problem: we can only call const member functions through a reference to const-qualified types, but `getReal` and `getImag` are not const-qualified, so there will still be a compilation error. Thus, `getReal` and `getImag` should also be declared to be const-qualified.
In addition, as for copy constructors, for the same reason, the parameters of most copy constructors should be references to const-qualified types.
**Not finished...**
## When should we use `const`?
If a member function doesn't change the members of the object, don't hesitate to add `const`; if a function parameter which is a pointer or a reference does not change the object it pointers to or refers to, don't hesitate to add `const`.
**Not finished...**
## Constant expressions
**Not finished...**
## One definition rule (ODR) and odr-used
**...**
8 changes: 4 additions & 4 deletions blogs/zh-CN/c_cpp/why-const-is-essential-in-cpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
## 前言

本文章是为了补充某大学某专业的程序设计课程而写,因此内容可能较为浅薄,请读者见谅。
本文章是为了补充某大学某专业的程序设计课程而写,因此内容可能较为浅薄,请读者见谅。

## 缺少了 `const` 会怎么样?

有某些大学程序设计课上经常看到诸如这样的代码:
有某些大学程序设计课上经常看到诸如这样的代码:

```c++
class Complex
Expand Down Expand Up @@ -48,7 +48,7 @@ double cabs(Complex& z)
}
```

有一天我不是很爽,写了这样的代码调用这个函数:`cabs(Complex(0.0, 0.0))`。结果非常 Amazing 啊!编译错误(然而这东西在 VS2008 上貌似并不会编译错误,让我们不考虑 VS2008)。为什么呢?我们所说的传统意义上的“引用”,只能绑定到[左值](./lvalue-and-rvalue.md)(在 C++11 出现右值引用之后,原来的引用也被称作左值引用),而 `Complex(0.0, 0.0)` 这是个右值表达式,因此并没有办法被引用。那怎么改呢?一个方法是把参数改成 `Complex z`。但是如果对于一些其他的类型,复制其对象可能会引来很大的开销,或者是该类型的复制构造函数不可访问或[已经定义为删除](https://zh.cppreference.com/w/cpp/language/function#.E5.BC.83.E7.BD.AE.E5.87.BD.E6.95.B0),因此我们还是希望参数为引用。那么,`const` 就派上用场了。[对有 `const` 限定(但没有 `volatile` 限定)的类型的左值引用既可以绑定到左值,又可以绑定到右值](https://zh.cppreference.com/w/cpp/language/reference_initialization)。所以函数改为:
有一天我不是很爽,写了这样的代码调用这个函数:`cabs(Complex(0.0, 0.0))`。结果非常 Amazing 啊!编译错误(然而这东西在 VS2008 上貌似并不会编译错误,让我们不考虑 VS2008)。为什么呢?我们所说的传统意义上的“引用”,只能使用[左值表达式](./lvalue-and-rvalue.md)初始化(在 C++11 出现右值引用之后,原来的引用也被称作左值引用),而 `Complex(0.0, 0.0)` 这是个右值表达式,因此并没有办法被引用。那怎么改呢?一个方法是把参数改成 `Complex z`。但是如果对于一些其他的类型,复制其对象可能会引来很大的开销,或者是该类型的复制构造函数不可访问或[已经定义为删除](https://zh.cppreference.com/w/cpp/language/function#.E5.BC.83.E7.BD.AE.E5.87.BD.E6.95.B0),因此我们还是希望参数为引用。那么,`const` 就派上用场了。[对有 `const` 限定(但没有 `volatile` 限定)的类型的左值引用既可以使用左值表达式初始化,又可以使用右值表达式初始化](https://zh.cppreference.com/w/cpp/language/reference_initialization)。所以函数改为:

```c++
double cabs(const Complex& z)
Expand All @@ -65,7 +65,7 @@ double cabs(const Complex& z)
## 什么时候该加 `const`?
如果一个成员函数不会改变类内的成员,那么请毫不犹豫地加上 `const`;如果一个以指针或引用传递的函数参数不会改变其引用或指向的对象,那么也请毫不犹豫地加上 `const`。
如果一个成员函数不会改变对象的成员,那么请毫不犹豫地加上 `const`;如果一个以指针或引用传递的函数参数不会改变其引用或指向的对象,那么也请毫不犹豫地加上 `const`。
**未完待续……**
Expand Down
4 changes: 2 additions & 2 deletions blogs/zh-TW/c_cpp/why-const-is-essential-in-cpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ double cabs(Complex& z)
}
```

有一天我不是很爽,寫了這樣的程式碼呼叫這個函式:`cabs(Complex(0.0, 0.0))`。結果非常 Amazing 啊!編譯錯誤(然而這東西在 VS2008 上貌似并不會編譯錯誤,讓我們不考慮 VS2008)。為什麽呢?我們所説的傳統意義上的“參考”,只能繫結到 [lvalue](./lvalue-and-rvalue.md)(在 C++11 出現右值參考之後,原來的參考也被稱作左值參考),而 `Complex(0.0, 0.0)` 這是個右值運算式,因此并沒有辦法被參考。那怎麽改呢?一個方法是把參數改成 `Complex z`。但是如果對於一些其他的類型,複製其物件可能會引來很大的負荷,或者是該類型的複製建構函式不可存取或[已經定義為刪除](https://zh.cppreference.com/w/cpp/language/function#.E5.BC.83.E7.BD.AE.E5.87.BD.E6.95.B0),因此我們還是希望參數為參考。那麽,`const` 就派上用場了。[對有 `const` 限定(但沒有 `volatile` 限定)的類型的左值參考既可以繫結到左值,又可以繫結到右值](https://zh.cppreference.com/w/cpp/language/reference_initialization)。所以函式改為:
有一天我不是很爽,寫了這樣的程式碼呼叫這個函式:`cabs(Complex(0.0, 0.0))`。結果非常 Amazing 啊!編譯錯誤(然而這東西在 VS2008 上貌似并不會編譯錯誤,讓我們不考慮 VS2008)。為什麽呢?我們所説的傳統意義上的“參考”,只能使用 [lvalue 運算式](./lvalue-and-rvalue.md)初始化(在 C++11 出現右值參考之後,原來的參考也被稱作左值參考),而 `Complex(0.0, 0.0)` 這是個右值運算式,因此并沒有辦法被參考。那怎麽改呢?一個方法是把參數改成 `Complex z`。但是如果對於一些其他的類型,複製其物件可能會引來很大的負荷,或者是該類型的複製建構函式不可存取或[已經定義為刪除](https://zh.cppreference.com/w/cpp/language/function#.E5.BC.83.E7.BD.AE.E5.87.BD.E6.95.B0),因此我們還是希望參數為參考。那麽,`const` 就派上用場了。[對有 `const` 限定(但沒有 `volatile` 限定)的類型的左值參考既可以使用左值運算式初始化,又可以使用右值運算式初始化](https://zh.cppreference.com/w/cpp/language/reference_initialization)。所以函式改為:

```c++
double cabs(const Complex& z)
Expand All @@ -65,7 +65,7 @@ double cabs(const Complex& z)
## 什麽時候該加 `const`?
如果一個成員函式不會改變類別内的成員,那麽請毫不猶豫地加上 `const`;如果一個以指標或參考傳遞的函式參數不會改變其參考或指向的物件,那麽也請毫不猶豫地加上 `const`。
如果一個成員函式不會改變物件的成員,那麽請毫不猶豫地加上 `const`;如果一個以指標或參考傳遞的函式參數不會改變其參考或指向的物件,那麽也請毫不猶豫地加上 `const`。
**未完待續……**
Expand Down

0 comments on commit edc076d

Please sign in to comment.