不可拷貝類的設計
我們知道有些對象 (比如文件流) 的拷貝與移動是沒有意義的,那我們應該怎樣禁止該類對象的拷貝和移動操作呢?有兩種途徑:
- 私有化聲明拷貝和移動類成員函數
- 公開化刪除拷貝和移動類成員函數 (C++11)
代碼示例 :
#include
class Noncopyable {
protected:
#if __cplusplus >= 201103L
Noncopyable() = default;
~Noncopyable() = default;
#else
Noncopyable() {}
~Noncopyable() {}
#endif
#if __cplusplus >= 201103L
Noncopyable(const Noncopyable &) = delete;
Noncopyable &operator=(const Noncopyable &) = delete;
#else
private:
Noncopyable(const Noncopyable &);
Noncopyable &operator=(const Noncopyable &);
#endif
};
class Job : Noncopyable {};
int main(void) {
Job j11 = Job{}; // works, C++17 copy elision
// Job j21 = j11; // calls to delete copy ctor, error compile
Job j22;
// j22 = Job{}; // calls to delete copy assignment, error compile
return 0;
}
公開化刪除拷貝和移動類成員函數:可以直接放到 public 域下更好的展現出來,并且編譯錯誤信息友好
私有化聲明拷貝和移動類成員函數:只有聲明沒有定義,編譯錯誤會延遲到鏈接階段,并且提示信息不友好
這里的 Noncopyable 基類修改自 Boost 庫的 noncopyable 類,protected 的使用,使其只能作為基類使用
普通函數刪除
已經公開了的函數,別人可能在使用,冒然刪除,不夠友好,實際上我們可以先給出廢棄警告,給別人一些緩沖時間來做修改,然后在一個未來的版本中徹底刪除。
代碼示例 :
// deletefn.cpp
#include
// deprecated a function
[[deprecated("use GoodLuckyExt instead")]] void GoodLucky() {
std::cout << "GoodLucky\n";
}
void GoodLuckyExt() { std::cout << "GoodLuckyExt\n"; }
// char, bool, double can implicitly convert to int
// delete functions
bool isLucky(int) { return true; }; // original function
bool isLucky(char) = delete; // reject chars
bool isLucky(bool) = delete; // reject bools
bool isLucky(double) = delete; // reject doubles and floats
int main(void) {
// deprecated warning
GoodLucky();
// function deleted
isLucky(2); // works
// isLucky(2.0); // not compiles, calls to delete function
// isLucky(true); // not compiles, calls to delete function
return 0;
}
$ clang++ deletefn.cpp -std=c++17
deletefn.cpp:19:3: warning: 'GoodLucky' is deprecated: use GoodLuckyExt instead [-Wdeprecated-declarations]
模板函數刪除
模板函數的刪除和普通函數的刪除類似,實際上就是刪除一些特化,偏特化的函數版本,這個技術也比較有用,能減少模板函數的實例化數量。
代碼示例 :
#include
// function template
template void processPointer(T *ptr) { std::cout << ptr << '\n'; }
// delete specialized instantiations
template <> void processPointer(void *ptr) = delete;
template <> void processPointer(char *ptr) = delete;
// function template in class
struct Job {
template void processPointer(T *ptr) { std::cout << ptr << '\n'; }
};
// delete specialized instantiations outside of class, still as public
template <> void Job::processPointer(void *ptr) = delete;
template <> void Job::processPointer(char *ptr) = delete;
int main(void) {
int i{0};
char c{'c'};
processPointer(&i); // 0x7ffee8be4c08
// processPointer(&c); // call to delete function
Job j;
j.processPointer(&i); // 0x7ffee8be4c08
// j.processPointer(&c); // call to delete member function
return 0;
}
私有化對類的模板成員函數無能為力,只能在類外使用 delete 刪除指定的實例化版本
總結
- delete 能刪除類的特殊成員函數,也能刪除類的模板成員函數
- delete 能刪除自由函數,也能刪除自由模板函數
- 刪除一個公開的函數之前,可以給出廢棄警告,得益于現代 C++ 的 [[deprecated]] 屬性
|