[英]What are the advantages of boost::noncopyable
為了防止復制一個類,你可以很容易地聲明一個私有的復制構造函數/賦值運算符。 但是你也可以繼承boost::noncopyable
。
在這種情況下使用 boost 的優點/缺點是什么?
我看不到文檔方面的好處:
#include <boost/noncopyable.hpp>
struct A
: private boost::noncopyable
{
};
對比:
struct A
{
A(const A&) = delete;
A& operator=(const A&) = delete;
};
當您添加僅移動類型時,我什至認為文檔具有誤導性。 以下兩個示例是不可復制的,但它們是可移動的:
#include <boost/noncopyable.hpp>
struct A
: private boost::noncopyable
{
A(A&&) = default;
A& operator=(A&&) = default;
};
對比:
struct A
{
A(A&&) = default;
A& operator=(A&&) = default;
};
在多重繼承下,甚至會有空間損失:
#include <boost/noncopyable.hpp>
struct A
: private boost::noncopyable
{
};
struct B
: public A
{
B();
B(const B&);
B& operator=(const B&);
};
struct C
: public A
{
};
struct D
: public B,
public C,
private boost::noncopyable
{
};
#include <iostream>
int main()
{
std::cout << sizeof(D) << '\n';
}
對我來說,這會打印出來:
3
但是,我相信它具有出色的文檔:
struct A
{
A(const A&) = delete;
A& operator=(const A&) = delete;
};
struct B
: public A
{
B();
B(const B&);
B& operator=(const B&);
};
struct C
: public A
{
C(const C&) = delete;
C& operator=(const C&) = delete;
};
struct D
: public B,
public C
{
D(const D&) = delete;
D& operator=(const D&) = delete;
};
#include <iostream>
int main()
{
std::cout << sizeof(D) << '\n';
}
輸出:
2
我發現聲明我的復制操作比推理我是否多次從boost::non_copyable
派生以及這是否會花費我要容易得多。 特別是如果我不是完整繼承層次結構的作者。
它使意圖明確明確,否則必須查看類的定義,並搜索與復制語義相關的聲明,然后查找聲明它的訪問說明符,以確定是否類是否不可復制。 通過編寫需要啟用復制語義的代碼並查看編譯錯誤來發現它的其他方法。
總結一下別人說的:
boost::noncopyable
對於私有復制方法的優點:
noncopyable
需要更長的時間才能發現的習慣用法。 私有復制方法相對於boost::noncopyable
優點:
我不明白為什么其他人似乎沒有提到它,但是:
使用noncopyable
,您只需寫一次類的名稱。
沒有,五重重復:一個 A 代表“類 A”,兩個禁用賦值,兩個禁用復制構造函數。
引用文檔:
“處理這些問題的傳統方法是聲明一個私有復制構造函數和復制賦值,然后記錄為什么要這樣做。但從 noncopyable 派生更簡單、更清晰,不需要額外的文檔。”
http://www.boost.org/libs/utility/utility.htm#Class_noncopyable
一個具體的優點(除了稍微更清楚地表達您的意圖之外)是,如果成員或友元函數試圖復制對象,則在編譯階段而不是鏈接階段會更快地捕獲錯誤。 基類構造函數/賦值在任何地方都無法訪問,從而導致編譯錯誤。
它還可以防止您意外定義函數(即鍵入{}
而不是;
),這是一個很可能不會被注意到的小錯誤,但它會允許成員和朋友制作對象的無效副本。
一個小缺點(特定於 GCC)是,如果你用g++ -Weffc++
編譯你的程序並且你有包含指針的類,例如
class C : boost::noncopyable
{
public:
C() : p(nullptr) {}
private:
int *p;
};
GCC 不明白發生了什么:
警告:“C 類”具有指針數據成員 [-Weffc++]
警告:但不會覆蓋'C(const S&)' [-Weffc++]
警告:或'operator=(const C&)' [-Weffc++]
雖然它不會抱怨:
#define DISALLOW_COPY_AND_ASSIGN(Class) \
Class(const Class &) = delete; \
Class &operator=(const Class &) = delete
class C
{
public:
C() : p(nullptr) {}
DISALLOW_COPY_AND_ASSIGN(C);
private:
int *p;
};
PS 我知道 GCC 的 -Weffc++ 有幾個問題。 無論如何,檢查“問題”的代碼非常簡單……有時它會有所幫助。
優點是您不必自己編寫私有復制構造函數和私有復制運算符,並且無需編寫額外的文檔即可清楚地表達您的意圖。
我寧願使用 boost::noncopyable 而不是手動刪除或私有化復制構造函數和賦值運算符。
但是,我幾乎從不使用任何一種方法,因為:
如果我正在制作一個不可復制的對象,則必須有一個不可復制的原因。 這個原因,在 99% 的情況下,是因為我的成員無法被有意義地復制。 很有可能,這些成員也更適合作為私有實現細節。 所以我制作了大多數這樣的課程:
struct Whatever {
Whatever();
~Whatever();
private:
struct Detail;
std::unique_ptr<Detail> detail;
};
所以現在,我有一個私有實現結構,並且由於我使用了 std::unique_ptr,因此我的頂級類是不可免費復制的。 由此產生的鏈接錯誤是可以理解的,因為它們討論了如何無法復制 std::unique_ptr。 對我來說,這就是 boost::noncopyable 和私有實現合二為一的所有好處。
這種模式的好處是后來,如果我決定我確實想讓我的這個類的對象可復制,我可以只添加和實現一個復制構造函數和/或賦值運算符,而無需更改類層次結構。
根據 Scott Meyers 的說法,如果您確實需要找到它的缺點,則該名稱是“非自然”的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.