C++ primer 第十三章复习 13.1

简介: C++ primer 第十三章复习 13.1

C++ primer 第十三章复习

13.1 拷贝,赋值与销毁

类 有五种特殊成员函数控制对象拷贝,移动,赋值和销毁


拷贝构造函数

拷贝赋值运算符

移动构造函数

移动赋值运算符

析构函数

上述操作称为拷贝控制操作,若一个类没有定义这些函数,编译器会自动生成缺失的函数


拷贝构造函数

拷贝构造函数通常不应该是 explicit ( explicit 不可以隐式初始化对象)


class Foo{
public:
  Foo(){} //构造函数
  Foo(const Foo&){} //拷贝构造函数
};



在C++中若构造函数只有一个参数, 那么在编译时会有一个缺省的转换:将该构造函数对应数据类型数据转换为该类对象,例如


CxString string2 = 10


//转换为

CxString string2(10);  

或  

CxString temp(10);  

CxString string2 = temp;


#include <iostream>using namespace std;
class Test{
public:
  Test(){}
  //拷贝构造函数
  Test(const Test &t){
    cout << "in copy" << endl;
    data = t.data;
  }
  Test& Test::operator=(const Test &t){
    std::cout << "in =" << std::endl;
    return *this;
  }
private:
  int data;
};
int main(){
  Test t1;
  Test t2(t1);//由于是显式调用拷贝构造函数,所以编译过
  Test t3;
  t3 = t2;
  Test t4 = t2;//由于是隐式调用拷贝构造函数,所以编译不过
  return 0;
}
//没有初始化对象无法调用 = 操作符函数(),所以如果要走 = 操作符函数,那么需要 Test t4; t4 = t2; 两步.故 Test t4 = t2 会被编译器优化为 Test t4(t2)

合成拷贝构造函数

涉及到其它类的拷贝,拷贝构造函数内部会调用其它类的拷贝构造


class Sales_data{
  public:
  Sales_data(const Sales_data&);
  private:
  std::string bookNo;
  int units_sold =0;
};
Sales_data::Sales_data(const Sales_data& orig):
  bookNo(orig.bookNo),  //调用string的拷贝构造函数
  units_sold(orig.units_sold){}

拷贝初始化

拷贝构造函数用来初始化非引用类型参数,所以自己的参数必须是引用(如果不是引用,又会调用参数的拷贝构造)


std::string dots(10, '.'); //构造函数初始化
std::string str1(dots); //显式拷贝构造函数初始化
std::string str2 = dots; //隐式拷贝构造函数初始化
std::string str3 ="9-99-999"; //构造函数初始化
std::string str4 = std::string(100, '9');//显式构造函数 + 隐式拷贝构造函数初始化

拷贝初始化的限制

explicit 单参构造函数,无法使用 = 成员值进行拷贝构造,作为函数参数同理


explicit vector(size_type n); //vec的构造函数
vector (const vector& x); //vec的拷贝构造函数
std::vector<int> v1(10); //正确,构造函数初始化
std::vector<int> v2 =10; //错误,构造函数是 explicit 的
void f(std::vector<int>);
f(10);  //错误,构造函数是 explicit 的
f(std::vector<int>(10)); //正确, std::vector<int> tmp =  std::vector<int>(10);

编译绕过拷贝构造函数

std::string str3 ="9-99-999";
//改写为
std::string str3("9-99-999");
//直接走构造函数初始化,而不会走隐式构造函数 + 隐式拷贝构造函数初始化

拷贝赋值运算符

拷贝赋值运算符接受一个调用类相同类型的参数


class Foo{
public:
  Foo(){}
  Foo(const Foo&){}
  //拷贝赋值运算符接受一个调用类相同类型的参数
  Foo& operator==(const Foo&){}
};
int main(){
    Foo f2;
    Foo f1;
    f1 = f2;
}


合成拷贝赋值运算符

涉及到其它类的拷贝运算符,拷贝运算符会调用其它类的拷贝运算符


class Sales_data{
  public:
  Sales_data(const Sales_data&);
  Sales_data& operator=(const Sales_data&);
  private:
  std::string bookNo;
  int units_sold =0;
};
Sales_data& Sales_data::operator=(const Sales_data&){
    bookNo = orig.bookNo;
    units_sold = orig.units_sold; //调用 string 的拷贝运算符
    return *this;
}

析构函数

不接受参数,不允许重载


构造函数初始化对象的非 static 数据成员 (先初始化成员再构造函数 父 -> 子)


析构函数释放对象成员,并销毁非 static 数据成员(先调用析构函数,再释放成员 子 -> 父)


先调用 A 析构,再销毁 A 数据成员 ,最后销毁父类 B,和创建刚好相反


class Foo{
public:
  ~Foo(){}
};

1、成员初始化顺序:只与类成员的声明顺序有关


2、初始化列表与构造函数体内初始化的区别:(内置数据类型除外)


在成员初始化列表中初始化,和在构造函数体内赋值,内置数据类型,复合类型(指针,引用)性能和结果完全一样,但用户定义类型(类类型)结果相同,但性能上存在差别。因类类型的数据成员对象在进入函数体前已构造完成,在成员初始化列表处进行构造对象工作,若在构造函数体内赋值相当于先进行构造函数再调用拷贝运算符,而成员初始化列表只调用拷贝构造函数即可


合成析构函数

在类的析构函数结束后,会自动调用成员的析构函数


class Foo{
public:
    //成员会自动销毁,不需要额外动作
  ~Foo(){} 
private:
  std::string data;
};

需要析构函数的类也需要拷贝和赋值操作

析构释放指针的类,需要拷贝函数和赋值操作;需要拷贝操作的类,也需要赋值操作,反之亦然


对一个类的每个对象分配一个唯一的 ID :需要自定义拷贝构造函数和赋值运算符,但不需要自定义析构函数


class HashPtr{
public:
  //构造函数
  HashPtr(const std::string& s = std::string()) :
    data(new std::string(s)), num(0){}
  //编译器生成的拷贝函数
  HashPtr(const HashPtr& hashPtr){
    (*this).data = hashPtr.data;
    this->num = hashPtr.num;
  }
  //编译器生成的拷贝操作符
  const HashPtr& operator=(const HashPtr& hashPtr){
    (*this).data = hashPtr.data;
    this->num = hashPtr.num;
    return *this;
  }
  //析构函数
  ~HashPtr(){ delete data; }
private:
  std::string* data;
  int num;
};
HashPtr test(HashPtr hashPtr){ //调用编译器生成的拷贝构造 HashPtr hashPtr(p1)
  HashPtr ret = hashPtr; //调用编译器生成的拷贝操作符
  return ret; // hashPtr 对象和 ret 将被销毁
}
int main(){
  HashPtr p1("some values");
  test(p1); //调用结束后, p1.data 指向的内存被释放了
  HashPtr p2(p1); //现在 p1,p2 的 data 变成了野指针
  system("pause");
  return 0;
}

显式缺省函数

(=default) , default只能用于6个特殊成员函数,但delete可用于任何成员函数


=default 可以显式的要求编译器生成合成的版本


class Sales_data{
  public:
  Sales_data() = default;
  Sales_data(const Sales_data&) = default;
  Sales_data& operator=(const Sales_data&);
  ~Sales_data() = default;
  private:
  std::string m_data;
};
// = default 生成的赋值运算符函数返回的对象类型是 Sales_data& 非 const
Sales_data& Sales_data::operator=(const Sales_data&) = default;

阻止拷贝

例,IOStream 类阻止拷贝,以免对个对象写入或读取相同的 IO 缓存


struct NoCopy
{
  NoCopy() = default;
  //通知编译器,不希望定义这些成员函数
  NoCopy(const NoCopy&) = delete; //阻止拷贝
  NoCopy& operator=(const NoCopy&) = delete;
  ~NoCopy() = default;
};
struct NoDtor
{
  NoDtor() = default;//默认构造
  ~NoDtor() = delete;//阻止析构
};


对于析构已删除的类,不能定义该类的对象或释放该类指针


struct NoDtor
{
  NoDtor() = default;//默认构造
  ~NoDtor() = delete;//阻止析构
};
int main(){
  //NoDtor nd; //报错,编译检测,因为对象不能正常销毁
  NoDtor* p = new NoDtor();
  delete p; //报错
  system("pause");
  return 0;
}

本质上,当不能 拷贝,赋值,删除 类成员时,类的合成拷贝函数已被定义为删除的


C++11 1前,阻止拷贝是通过 private 实现的,但是友元和成员函数仍可以拷贝


struct PrivateCopy
{
private:
  PrivateCopy(const PrivateCopy&);
  PrivateCopy& operator=(const PrivateCopy&);
public:
  //默认的合成构造函数,可使用类对象无法拷贝
  PrivateCopy() = default;
  ~PrivateCopy() = delete;
}

单例模式


实现 = delete 实现单例


template<typename T>
class Singleton
{
public:
    static T& GetInstance()
    {
        static T instance;
        return instance;
    }
    Singleton(T&&) = delete;
    Singleton(const T&) = delete;
    void operator= (const T&) = delete;
protected:
    Singleton() = default;
    virtual ~Singleton() =



相关文章
|
7月前
|
编译器 C++
c++primer plus 6 读书笔记 第十章 对象和类
c++primer plus 6 读书笔记 第十章 对象和类
|
7月前
|
编译器 数据安全/隐私保护 C++
c++primer plus 6 读书笔记 第十三章 类继承
c++primer plus 6 读书笔记 第十三章 类继承
|
7月前
|
C++
C++ Primer Plus (第6版)中文版 (使用XMind整理)
C++ Primer Plus (第6版)中文版 (使用XMind整理)
C++ Primer Plus (第6版)中文版 (使用XMind整理)
|
7月前
|
C++
c++primer plus 6 读书笔记 第十四章 C++中的代码重用
c++primer plus 6 读书笔记 第十四章 C++中的代码重用
|
7月前
|
C++
c++primer plus 6 读书笔记 第十一章 使用类
c++primer plus 6 读书笔记 第十一章 使用类
|
7月前
|
编译器 C++
c++primer plus 6 读书笔记 第八章 函数探幽0
c++primer plus 6 读书笔记 第八章 函数探幽0
|
7月前
|
编译器 vr&ar C++
c++primer plus 6 读书笔记 第七章 函数--C++的编程模块
c++primer plus 6 读书笔记 第七章 函数--C++的编程模块
|
7月前
|
SQL 人工智能 算法
技术心得记录:模板函数函数模板FunctionTemplate(C++Primer
技术心得记录:模板函数函数模板FunctionTemplate(C++Primer
|
7月前
|
程序员 C++
c++primer plus 6 读书笔记 第十二章 类和动态内存分配
c++primer plus 6 读书笔记 第十二章 类和动态内存分配
|
7月前
|
存储 IDE 编译器
c++primer plus 6 读书笔记 第九章 内存模型和名称空间
c++primer plus 6 读书笔记 第九章 内存模型和名称空间