【C++】C/C++ 中default/delete特性
C++類的特殊成員函數(shù)及default/delete特性
本文內(nèi)容側(cè)重個人理解,深入理解其原理推薦https://www.geeksforgeeks.org
目錄
目錄
C++類的特殊成員函數(shù)及default/delete特性
前言
1. 構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)
2. 拷貝賦值運(yùn)算符
3. C++11特性之default關(guān)鍵字(P237, P449)
4. C++11特性之delete關(guān)鍵字(P449):使用“=delete”來限制函數(shù)生成
參考
前言
拷貝,賦值與構(gòu)造?詳見
C++ 的類有四類特殊成員函數(shù),分別是:
-
默認(rèn)構(gòu)造函數(shù)
-
析構(gòu)函數(shù)
-
拷貝構(gòu)造函數(shù)
-
拷貝賦值運(yùn)算符
這些類的特殊成員函數(shù)負(fù)責(zé)創(chuàng)建、初始化、銷毀,或者拷貝類的對象。如果沒有顯式地為一個類定義某個特殊成員函數(shù),而又需要用到該特殊成員函數(shù)時,則編譯器會隱式的為這個類生成一個默認(rèn)的特殊成員函數(shù)。
sample
class Test{private:int _id;
?public:Test(int a); ?// 構(gòu)造函數(shù)Test(const Test& test); ?// 拷貝構(gòu)造函數(shù)Test& operator=(const Test& test); ?// 拷貝賦值運(yùn)算符~Test(); ?// 析構(gòu)函數(shù)
}
1. 構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)
從名稱上也不難理解,構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)都是用于進(jìn)行類實(shí)例初始化的
Test t1(2); ?// 調(diào)用實(shí)參匹配的構(gòu)造函數(shù)
Test t2 = t1; ?// 調(diào)用拷貝構(gòu)造函數(shù),用t1對t2進(jìn)行初始化
以上也正是C++類進(jìn)行初始化的兩種方式。
使用/調(diào)用拷貝構(gòu)造函數(shù)的情況
拷貝構(gòu)造函數(shù)的作用主要是復(fù)制對象
-
復(fù)制對象,并通過函數(shù)返回復(fù)制后的對象。
-
一個對象以
值傳遞的方式傳入函數(shù),此時必定調(diào)用拷貝構(gòu)造函數(shù),即void Func(Test test){}這類。 -
一個對象以另一個對象為例進(jìn)行初始化,以上
Test t2=t1;即是此例。
必須顯式定義拷貝構(gòu)造函數(shù)的情況
對于拷貝來講,尤其需要注意的即是指針和動態(tài)分配的資源,這些很容易造成拷貝變成淺拷貝(指向復(fù)制前后的變量指向同一塊內(nèi)存區(qū)域)。 因此如果類的成員變量包含指針類型,或者有成員表示在構(gòu)造函數(shù)中分配的其他資源,這兩種情況下都必須顯式的定義拷貝構(gòu)造函數(shù)。
2. 拷貝賦值運(yùn)算符
通過定義拷貝賦值運(yùn)算符,來實(shí)現(xiàn)類實(shí)例之間的=拷貝運(yùn)算。 拷貝賦值運(yùn)算符的通常形式為 classname& operator=(const classname& a)
拷貝賦值運(yùn)算符與拷貝構(gòu)造函數(shù) 以下例來觀察二者的不同:
Test t2 = t1; // 即上面的例子,調(diào)用的是拷貝構(gòu)造函數(shù),即在創(chuàng)建時進(jìn)行初始化
Test t3;
t3 = t1; ?// 此時調(diào)用拷貝賦值運(yùn)算符,因?yàn)椴⒉皇窃趧?chuàng)建類的實(shí)例時進(jìn)行初始化
可以看出二者之間有很大的共通性,即都是為了進(jìn)行完整的復(fù)制/拷貝而創(chuàng)立的,防止陷入淺拷貝造成內(nèi)存安全問題。
3. C++11特性之default關(guān)鍵字(P237, P449)
類內(nèi)是哦那個=default修飾成員變量時候,合成的函數(shù)隱式聲明成內(nèi)聯(lián)函數(shù)。如果不希望合成的成員是內(nèi)聯(lián)函數(shù),應(yīng)該只對成員的類外定義使用=default。
=default含義:
當(dāng)我們定義這個構(gòu)造函數(shù)的目的僅僅是我們既需要其他形式的構(gòu)造函數(shù),也需要默認(rèn)的構(gòu)造函數(shù),我們希望這個函數(shù)的作用等于之前使用的默認(rèn)構(gòu)造函數(shù)。如下
struct Sales_data{Sales_data()=default;Sales_data(const std::string &s):bookNo(s){}Sales_data(const std::string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(P*n){}Sales_data(const std::istream &)... ...}
構(gòu)造函數(shù)初始化列表
Sales_data(const std::string &s):bookNo(s),units_sold(0),revenue(0){}//
?
在未顯式的定義類的特殊成員函數(shù)時,如果被調(diào)用,系統(tǒng)會自動隱式的創(chuàng)建該特殊成員函數(shù),且隱式的創(chuàng)建方式比顯式的創(chuàng)建方式執(zhí)行效率高。
只需在函數(shù)聲明后加上=default;,就可將該函數(shù)聲明為 defaulted 函數(shù),編譯器將為顯式聲明的 defaulted 函數(shù)自動生成函數(shù)體,以獲得更高的執(zhí)行效率。
有些時候,我們需要禁用某些(通常用法為禁用類的成員函數(shù),如單例模式中的拷貝構(gòu)造函數(shù),與賦值函數(shù))函數(shù)(=delete不僅可以禁用類內(nèi)的特殊成員函數(shù),也可以禁用一般函數(shù)),此時就需要在該函數(shù)后面增加=delete;,則該函數(shù)將變的不可調(diào)用,比如不可復(fù)制等。
sample:
class Test{private:int _id;
?public:Test() = default; // 定義默認(rèn)構(gòu)造函數(shù)Test(int a);Test(const Test& test) = delete; ?// 禁止使用拷貝構(gòu)造函數(shù)的場景Test& operator=(const Test& test);~Test();
}
4. C++11特性之delete關(guān)鍵字(P449):使用“=delete”來限制函數(shù)生成
本質(zhì)上,這些規(guī)則的含義是:如果一個類有數(shù)據(jù)成員不能默認(rèn)構(gòu)造函數(shù),拷貝,賦值,或銷毀,則對應(yīng)的成員函數(shù)將被定義為刪除。
常見實(shí)例應(yīng)用為單例模式中的權(quán)限訪問設(shè)置。https://guoqiang.blog.csdn.net/article/details/115452089
本質(zhì)上,當(dāng)不可能拷貝 賦值 或者銷毀類的成員時,類的合成拷貝控制成員就被定義為刪除。
?
- 實(shí)例1: Employee 類需要定義自己的開唄控制函數(shù)嗎?為什么?P452??不需要,因?yàn)檎娴臎]有任何合理的意義。員工不能在現(xiàn)實(shí)世界中模仿別人。
#include <string>
using std::string;class Employee {
public:Employee();Employee(const string &name);Employee(const Employee&) = delete;Employee& operator=(const Employee&) = delete;const int id() const { return id_; }private:string name_;int id_;static int s_increment;
};
- 實(shí)例2: 我們在類中實(shí)現(xiàn)了這些版本之后,編譯器便不會生成其對應(yīng)的默認(rèn)函數(shù)版本,這時需要我們顯式的寫上其對應(yīng)的默認(rèn)函數(shù)版本。??
#include<iostream>
using namespace std;
class Student
{
public:Student(const int a,const int b):m_a(a),m_b(b){
?}
?
int getA()const{return m_a;}
int getB()const{return m_b;}
private:
int m_a;
int m_b;
};
?
int main(int argc,char **argv)
{
Student stu(1,2);
cout<<stu.getA()<<endl; //1
cout<<stu.getB()<<endl; //2
?Student stu1; ? ? ? ? ? //編譯失敗,報錯: no matching function for call to ‘Student::Student()’
?
return 0;
}
?
編譯方式:g++ Student.cpp
例1定義了一個對象stu1,該對象將會使用Student類的無參構(gòu)造函數(shù),而該默認(rèn)構(gòu)造函數(shù)在Student類中,我們沒有顯式的說明。因此,c++編譯器在我們提供了該函數(shù)實(shí)現(xiàn)之后是不會生成與之對應(yīng)的默認(rèn)函數(shù)版本的。在Student中我們重載了帶2個參數(shù)的構(gòu)造函數(shù),但是無參的構(gòu)造函數(shù),沒有提供,因此會報錯。
??解決方式是:在該類中顯式的提供無參構(gòu)造函數(shù),如下:
- 實(shí)例3:
#include<iostream>
using namespace std;
class Student
{
public:Student(){} ? //顯式說明Student的無參構(gòu)造函數(shù)Student(const int a,const int b):m_a(a),m_b(b){
?}
?
int getA()const{return m_a;}
int getB()const{return m_b;}
private:
int m_a;
int m_b;
};
?
int main(int argc,char **argv)
{
Student stu(1,2);
cout<<stu.getA()<<endl; //1
cout<<stu.getB()<<endl; //2
?Student stu1;
return 0;
}
問題:以 Student(){} 這樣的方式來聲明無參數(shù)構(gòu)造函數(shù),會帶來一個問題,就是使得 其不再是 POD 類型,因此可能讓編譯器失去對這樣的數(shù)據(jù)類型的優(yōu)化功能。這是我們不希望看到的。因此最好使用 = default來修飾默認(rèn)構(gòu)造函數(shù)。
實(shí)例3:
C++開發(fā)中,我們經(jīng)常需要控制某些函數(shù)的生成。在C++11之前,我們經(jīng)常的普遍做法是將其聲明為類的 private 成員函數(shù),這樣若在類外這些這些函數(shù)的操作時候,編譯器便會報錯,從而達(dá)到效果。 如例1:
#include<iostream>
using namespace std;
class Student
{
public:Student() = default;Student(const int a,const int b):m_a(a),m_b(b) { }
?
int getA()const{return m_a;}
int getB()const{return m_b;}
?
private:Student(const Student& );Student& operator =(const Student& );
?
private:int m_a;int m_b;
};
?
int main(int argc,char **argv)
{
Student stu(1,2);
cout<<stu.getA()<<endl; //1
cout<<stu.getB()<<endl; //2
?
//Student stu1(stu);
//報錯:Student.cpp:26:5: error: ‘Student::Student(const Student&)’ is private
?
//Student stu1(3,4);
//stu1 = stu;
//報錯:Student.cpp:27:14: error: ‘Student& Student::operator=(const Student&)’ is private
?
std::cout<<is_pod<Student>::value<<std::endl; //
return 0;
}
例1代碼編譯報錯,因?yàn)樵陬愔?#xff0c;我們將Student的拷貝構(gòu)造函數(shù)和拷貝賦值函數(shù)都聲明為了 private 屬性,因此,當(dāng)在類外使用拷貝構(gòu)造和拷貝賦值操作值,編譯器會報錯。雖然能夠達(dá)到效果,但是不夠直觀和簡潔。對于追求高效以及簡潔來說,這樣做有2個問題:?? ?
?(1)不是最簡化;
?(2)對于友元支持不友好.?更為簡潔直觀的方法是使用:=delete
#include<iostream>
using namespace std;
class Student
{
public:Student() = default;Student(const int a,const int b):m_a(a),m_b(b){
?}
?
int getA()const{return m_a;}
int getB()const{return m_b;}
?Student(const Student& ) = delete;Student& operator =(const Student& ) = delete;
?
private:
int m_a;
int m_b;
};
?
int main(int argc,char **argv)
{
Student stu(1,2);
cout<<stu.getA()<<endl; //1
cout<<stu.getB()<<endl; //2
?
//Student stu1(stu);
//報錯:Student.cpp:39:21: error: use of deleted function ‘Student::Student(const Student&)’
?
//Student(const Student& );
?
//Student stu1(3,4);
//stu1 = stu;
//報錯:SStudent.cpp:44:10: error: use of deleted function ‘Student& Student::operator=(const Student&)’
?
std::cout<<is_pod<Student>::value<<std::endl; //
return 0;
}
注:若缺省版本被刪除了,重載該函數(shù)是非法的.
?
參考
- https://guoqiang.blog.csdn.net/article/details/115452089
- https://en.cppreference.com/w/cpp/language/member_functions#Special_member_functions
- https://en.cppreference.com/w/cpp/language/function#Deleted_functions
總結(jié)
以上是生活随笔為你收集整理的【C++】C/C++ 中default/delete特性的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: p50pro屏幕供应商?
- 下一篇: 【C++】C/C++ 中多态情形下的虚函