【C++】C/C++ 中default/delete特性
C++類的特殊成員函數及default/delete特性
本文內容側重個人理解,深入理解其原理推薦https://www.geeksforgeeks.org
目錄
目錄
C++類的特殊成員函數及default/delete特性
前言
1. 構造函數和拷貝構造函數
2. 拷貝賦值運算符
3. C++11特性之default關鍵字(P237, P449)
4. C++11特性之delete關鍵字(P449):使用“=delete”來限制函數生成
參考
前言
拷貝,賦值與構造?詳見
C++ 的類有四類特殊成員函數,分別是:
-  默認構造函數 
-  析構函數 
-  拷貝構造函數 
-  拷貝賦值運算符 
這些類的特殊成員函數負責創建、初始化、銷毀,或者拷貝類的對象。如果沒有顯式地為一個類定義某個特殊成員函數,而又需要用到該特殊成員函數時,則編譯器會隱式的為這個類生成一個默認的特殊成員函數。
sample
class Test{private:int _id;
?public:Test(int a); ?// 構造函數Test(const Test& test); ?// 拷貝構造函數Test& operator=(const Test& test); ?// 拷貝賦值運算符~Test(); ?// 析構函數
}1. 構造函數和拷貝構造函數
從名稱上也不難理解,構造函數和拷貝構造函數都是用于進行類實例初始化的
Test t1(2); ?// 調用實參匹配的構造函數
Test t2 = t1; ?// 調用拷貝構造函數,用t1對t2進行初始化以上也正是C++類進行初始化的兩種方式。
使用/調用拷貝構造函數的情況
拷貝構造函數的作用主要是復制對象
-  復制對象,并通過函數返回復制后的對象。 
-  一個對象以 值傳遞的方式傳入函數,此時必定調用拷貝構造函數,即void Func(Test test){}這類。
-  一個對象以另一個對象為例進行初始化,以上 Test t2=t1;即是此例。
必須顯式定義拷貝構造函數的情況
   對于拷貝來講,尤其需要注意的即是指針和動態分配的資源,這些很容易造成拷貝變成淺拷貝(指向復制前后的變量指向同一塊內存區域)。   因此如果類的成員變量包含指針類型,或者有成員表示在構造函數中分配的其他資源,這兩種情況下都必須顯式的定義拷貝構造函數。
2. 拷貝賦值運算符
 通過定義拷貝賦值運算符,來實現類實例之間的=拷貝運算。 拷貝賦值運算符的通常形式為 classname& operator=(const classname& a)
拷貝賦值運算符與拷貝構造函數 以下例來觀察二者的不同:
Test t2 = t1; // 即上面的例子,調用的是拷貝構造函數,即在創建時進行初始化
Test t3;
t3 = t1; ?// 此時調用拷貝賦值運算符,因為并不是在創建類的實例時進行初始化可以看出二者之間有很大的共通性,即都是為了進行完整的復制/拷貝而創立的,防止陷入淺拷貝造成內存安全問題。
3. C++11特性之default關鍵字(P237, P449)
類內是哦那個=default修飾成員變量時候,合成的函數隱式聲明成內聯函數。如果不希望合成的成員是內聯函數,應該只對成員的類外定義使用=default。
=default含義:
當我們定義這個構造函數的目的僅僅是我們既需要其他形式的構造函數,也需要默認的構造函數,我們希望這個函數的作用等于之前使用的默認構造函數。如下
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 &)... ...}構造函數初始化列表
Sales_data(const std::string &s):bookNo(s),units_sold(0),revenue(0){}//
?
 在未顯式的定義類的特殊成員函數時,如果被調用,系統會自動隱式的創建該特殊成員函數,且隱式的創建方式比顯式的創建方式執行效率高。
 只需在函數聲明后加上=default;,就可將該函數聲明為 defaulted 函數,編譯器將為顯式聲明的 defaulted 函數自動生成函數體,以獲得更高的執行效率。
 有些時候,我們需要禁用某些(通常用法為禁用類的成員函數,如單例模式中的拷貝構造函數,與賦值函數)函數(=delete不僅可以禁用類內的特殊成員函數,也可以禁用一般函數),此時就需要在該函數后面增加=delete;,則該函數將變的不可調用,比如不可復制等。
sample:
class Test{private:int _id;
?public:Test() = default; // 定義默認構造函數Test(int a);Test(const Test& test) = delete; ?// 禁止使用拷貝構造函數的場景Test& operator=(const Test& test);~Test(); 
}4. C++11特性之delete關鍵字(P449):使用“=delete”來限制函數生成
本質上,這些規則的含義是:如果一個類有數據成員不能默認構造函數,拷貝,賦值,或銷毀,則對應的成員函數將被定義為刪除。
常見實例應用為單例模式中的權限訪問設置。https://guoqiang.blog.csdn.net/article/details/115452089
本質上,當不可能拷貝 賦值 或者銷毀類的成員時,類的合成拷貝控制成員就被定義為刪除。
?
- 實例1: Employee 類需要定義自己的開唄控制函數嗎?為什么?P452??不需要,因為真的沒有任何合理的意義。員工不能在現實世界中模仿別人。
#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;
};
- 實例2: 我們在類中實現了這些版本之后,編譯器便不會生成其對應的默認函數版本,這時需要我們顯式的寫上其對應的默認函數版本。??
#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類的無參構造函數,而該默認構造函數在Student類中,我們沒有顯式的說明。因此,c++編譯器在我們提供了該函數實現之后是不會生成與之對應的默認函數版本的。在Student中我們重載了帶2個參數的構造函數,但是無參的構造函數,沒有提供,因此會報錯。
??解決方式是:在該類中顯式的提供無參構造函數,如下:
- 實例3:
#include<iostream>
using namespace std;
class Student
{
public:Student(){} ? //顯式說明Student的無參構造函數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(){} 這樣的方式來聲明無參數構造函數,會帶來一個問題,就是使得 其不再是 POD 類型,因此可能讓編譯器失去對這樣的數據類型的優化功能。這是我們不希望看到的。因此最好使用 = default來修飾默認構造函數。
實例3:
C++開發中,我們經常需要控制某些函數的生成。在C++11之前,我們經常的普遍做法是將其聲明為類的 private 成員函數,這樣若在類外這些這些函數的操作時候,編譯器便會報錯,從而達到效果。 如例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代碼編譯報錯,因為在類中,我們將Student的拷貝構造函數和拷貝賦值函數都聲明為了 private 屬性,因此,當在類外使用拷貝構造和拷貝賦值操作值,編譯器會報錯。雖然能夠達到效果,但是不夠直觀和簡潔。對于追求高效以及簡潔來說,這樣做有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;
}注:若缺省版本被刪除了,重載該函數是非法的.
?
參考
- 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
總結
以上是生活随笔為你收集整理的【C++】C/C++ 中default/delete特性的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: p50pro屏幕供应商?
- 下一篇: 《梦仙》第四十一句是什么
