1. 程式人生 > 實用技巧 >CppCon筆記--Back to Basics: RAII and the Rule of Zero

CppCon筆記--Back to Basics: RAII and the Rule of Zero

1.RAII 和 rule of three

C++程式設計很多時候需要手動管理資源,其中包括資源的獲取,使用和釋放,而手動對資源釋放是很容易出錯的一個環節。

根據C++的特性,當局部物件的生命週期結束時,會呼叫解構函式,因此藉由類的解構函式對資源進行釋放就是RAII的工作原理。

但是這段程式碼仍然存在問題,如果對vector進行復制,此時的析構會進行double release,程式碼報錯。這裡就引入了C++的第一個rule of thumb。

當類直接對一些資源進行管理時,你需要手寫三個成員函式:

  • 解構函式,釋放資源
  • 拷貝函式,選擇正確的拷貝方式,是否深拷貝,是否釋放rhs的物件
  • 拷貝建構函式,同樣是選擇正確的拷貝方式

不要忘了有時候可以用swap-and-copy來處理,會更加安全。


比如這個例子self-assign會記憶體洩漏


這個例子中能self-assign,但是用成員資料賦值會出問題。

2.RAII 和異常安全

利用RAII的特性,對資源進行封裝。

nocopyable -> delete 拷貝賦值函式和拷貝建構函式

default -> 編譯器會自動生成拷貝或移動的建構函式

https://stackoverflow.com/questions/6502828/what-does-default-mean-after-a-class-function-declaration

3.Rule of zero

如果你的類沒有直接管理任何資源,僅僅使用標準庫,你不應該手寫任何特殊成員函式(構造器、拷貝賦值函式),應該把他們全部default了。

4.Rule of five

e of three

C++程式設計很多時候需要手動管理資源,其中包括資源的獲取,使用和釋放,而手動對資源釋放是很容易出錯的一個環節。

根據C++的特性,當局部物件的生命週期結束時,會呼叫解構函式,因此藉由類的解構函式對資源進行釋放就是RAII的工作原理。

但是這段程式碼仍然存在問題,如果對vector進行復制,此時的析構會進行double release,程式碼報錯。這裡就引入了C++的第一個rule of thumb。

當類直接對一些資源進行管理時,你需要手寫三個成員函式:

  • 解構函式,釋放資源
  • 拷貝函式,選擇正確的拷貝方式,是否深拷貝,是否釋放rhs的物件
  • 拷貝建構函式,同樣是選擇正確的拷貝方式

不要忘了有時候可以用swap-and-copy來處理,會更加安全。

noexcept!


比如這個例子self-assign會記憶體洩漏


這個例子中能self-assign,但是用成員資料賦值會出問題。

2.RAII 和異常安全

利用RAII的特性,對資源進行封裝。

nocopyable -> delete 拷貝賦值函式和拷貝建構函式

default -> 編譯器會自動生成拷貝或移動的建構函式

https://stackoverflow.com/questions/6502828/what-does-default-mean-after-a-class-function-declaration

3.Rule of zero

如果你的類沒有直接管理任何資源,僅僅使用標準庫,你不應該手寫任何特殊成員函式(構造器、拷貝賦值函式),應該把他們全部default了。

4.Rule of five

由於Cpp11引入了右值的概念,現在的rule of three已經有些滿足不了實際的使用了,所以引入了rule of five.

如果你的類直接對資源進行管理,你需要手寫5個特殊成員函式:

  • 解構函式
  • 拷貝建構函式
  • 移動建構函式
  • 拷貝賦值函式
  • 移動賦值函式

不過拷貝和移動建構函式可以被寫成同一個函式。(題外話,印象中有一期clean code講過,有的情況這樣會引起拷貝而不是移動,還是需要注意)

Foo& operator(Foo rhs/*通過值傳遞,左右值都能構建引數,並於this交換*/)
{
      swap(*this, rhs);
      return *this;
}

5.回來naive vector

Arthur還起了個rule of 4.5的名字233

5.0.友元函式

友元函式可以訪問這個類的所有成員變數。這裡其實不是友元成員函式(申明某類的某函式能訪問其的所有成員),而是在類裡面定義了一個友元函式。 https://en.cppreference.com/w/cpp/language/friend

  1. Designates a function or several functions as friends of this class
lass Y {
    int data; // private member
    // the non-member function operator<< will have access to Y's private members
    friend std::ostream& operator<<(std::ostream& out, const Y& o);
    friend char* X::foo(int); // members of other classes can be friends too
    friend X::X(char), X::~X(); // constructors and destructors can be friends
};
// friend declaration does not declare a member function
// this operator<< still needs to be defined, as a non-member
std::ostream& operator<<(std::ostream& out, const Y& y)
{
    return out << y.data; // can access private member Y::data
}
  1. (only allowed in non-local class definitions) Defines a non-member function, and makes it a friend of this class at the same time. Such non-member function is always inline.
class X {
    int a;
    friend void friend_set(X& p, int i) {
        p.a = i; // this is a non-member function
    }
 public:
    void member_set(int i) {
        a = i; // this is a member function
    }
};

5.1.std::exchange

把第一個引數賦值到lhs,把第二個引數作為第一個引數的新值

template<class T, class U = T>
constexpr // since C++20
T exchange(T& obj, U&& new_value)
{
    T old_value = std::move(obj);
    obj = std::forward<U>(new_value);
    return old_value;
}

e.g.

struct S
{
  int n;
  S(S&& other) noexcept : n{std::exchange(other.n, 0)} {}
  S& operator=(S&& other) noexcept 
  {
    if(this != &other)
        n = std::exchange(other.n, 0); // move n, while leaving zero in other.n
    return *this;
  }
};

利用unique_ptr來簡化rule of five

N.B. 這些rule都只和資源管理有關,所以類的實現同樣可以在rule of zero上加上其他的建構函式,比如initialize list。

6.一些資源管理的例子