有時(shí)讓類去做拷貝是沒有意義的。std::mutex
就是一個(gè)例子——拷貝一個(gè)互斥量,意義何在?std::unique_lock<>
是另一個(gè)例子——一個(gè)實(shí)例只能擁有一個(gè)鎖;如果要復(fù)制,拷貝的那個(gè)實(shí)例也能獲取相同的鎖,這樣std::unique_lock<>
就沒有存在的意義了。實(shí)例中轉(zhuǎn)移所有權(quán)(A.1.2節(jié))是有意義的,其并不是使用的拷貝。當(dāng)然其他例子就不一一列舉了。
通常為了避免進(jìn)行拷貝操作,會(huì)將拷貝構(gòu)造函數(shù)和拷貝賦值操作符聲明為私有成員,并且不進(jìn)行實(shí)現(xiàn)。如果對(duì)實(shí)例進(jìn)行拷貝,將會(huì)引起編譯錯(cuò)誤;如果有其他成員函數(shù)或友元函數(shù)想要拷貝一個(gè)實(shí)例,那將會(huì)引起鏈接錯(cuò)誤(因?yàn)槿鄙賹?shí)現(xiàn)):
class no_copies
{
public:
no_copies(){}
private:
no_copies(no_copies const&); // 無實(shí)現(xiàn)
no_copies& operator=(no_copies const&); // 無實(shí)現(xiàn)
};
no_copies a;
no_copies b(a); // 編譯錯(cuò)誤
在C++11中,委員會(huì)意識(shí)到這種情況,但是沒有意識(shí)到其會(huì)帶來攻擊性。因此,委員會(huì)提供了更多的通用機(jī)制:可以通過添加= delete
將一個(gè)函數(shù)聲明為刪除函數(shù)。
no_copise類就可以寫為:
class no_copies
{
public:
no_copies(){}
no_copies(no_copies const&) = delete;
no_copies& operator=(no_copies const&) = delete;
};
這樣的描述要比之前的代碼更加清晰。也允許編譯器提供更多的錯(cuò)誤信息描述,當(dāng)成員函數(shù)想要執(zhí)行拷貝操作的時(shí)候,可將連接錯(cuò)誤轉(zhuǎn)移到編譯時(shí)。
拷貝構(gòu)造和拷貝賦值操作刪除后,需要顯式寫一個(gè)移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值操作符,與std::thread
和std::unique_lock<>
一樣,你的類是只移動(dòng)的。
下面清單中的例子,就展示了一個(gè)只移動(dòng)的類。
清單A.2 只移動(dòng)類
class move_only
{
std::unique_ptr<my_class> data;
public:
move_only(const move_only&) = delete;
move_only(move_only&& other):
data(std::move(other.data))
{}
move_only& operator=(const move_only&) = delete;
move_only& operator=(move_only&& other)
{
data=std::move(other.data);
return *this;
}
};
move_only m1;
move_only m2(m1); // 錯(cuò)誤,拷貝構(gòu)造聲明為“已刪除”
move_only m3(std::move(m1)); // OK,找到移動(dòng)構(gòu)造函數(shù)
只移動(dòng)對(duì)象可以作為函數(shù)的參數(shù)進(jìn)行傳遞,并且從函數(shù)中返回,不過當(dāng)想要移動(dòng)左值,通常需要顯式的使用std::move()
或static_cast<T&&>
。
可以為任意函數(shù)添加= delete
說明符,添加后就說明這些函數(shù)是不能使用的。當(dāng)然,還可以用于很多的地方;刪除函數(shù)可以以正常的方式參與重載解析,并且如果被使用只會(huì)引起編譯錯(cuò)誤。這種方式可以用來刪除特定的重載。比如,當(dāng)函數(shù)以short作為參數(shù),為了避免擴(kuò)展為int類型,可以寫出重載函數(shù)(以int為參數(shù))的聲明,然后添加刪除說明符:
void foo(short);
void foo(int) = delete;
現(xiàn)在,任何向foo函數(shù)傳遞int類型參數(shù)都會(huì)產(chǎn)生一個(gè)編譯錯(cuò)誤,不過調(diào)用者可以顯式的將其他類型轉(zhuǎn)化為short:
foo(42); // 錯(cuò)誤,int重載聲明已經(jīng)刪除
foo((short)42); // OK