前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一个面试题引发的思考——类的特种成员函数

一个面试题引发的思考——类的特种成员函数

作者头像
程序员的园
发布2024-07-18 13:36:31
120
发布2024-07-18 13:36:31
举报
文章被收录于专栏:程序员的园——原创文章

之前问面试者“定义一个空类,并声明该类的多个对象,为什么对象间可以相互赋值?”本意是希望面试者能够回答编译期默认生成的构造函数、拷贝构造函数和拷贝赋值运算符函数。但是并没有回答到点子上。进一步引导到,“类的特种成员函数有哪些?”,也没有回答上来。有可能是我没有问清楚,也有可能是面试者由于紧张懵住了。今天刚好拿出这个问题来讨论下。

类的特种成员函数

关于特种成员函数,C++11前有四个:默认构造函数、析构函数、拷贝构造函数、拷贝赋值运算符。C++11开始新增了移动赋值运算符和移动构造函数,即C++11起存在6个特种成员函数。

  • 这些成员函数只有在代码中用到且没有声明时才会生成默认,即没有用到则不会生成。(见示例代码1)
  • 关于默认构造函数:当类中不存在构造函数时,才会生成默认构造函数。(见示例代码1)
  • 生成的默认函数时inline、非虚且为public访问级别,继承自父类的虚析构除外。(见示例代码1)
  • 移动构造和移动赋值会针对其非静态的成员(含基类部分)执行移动构造和移动赋值,但是移动构造和移动赋值只是移动请求;针对不可移型别将执行对应的拷贝动作
  • 拷贝构造函数和拷贝赋值运算符的生成相互独立,两者并无影响。(见示例代码2)
  • 移动构造和移动赋值相互影响,声明了其中一个就会阻止编译器生成另一个。声明移动构造会抑制移动赋值的生成;声明移动赋值运算符函数会抑制移动构造函数的生成。
  • 声明移动操作,则会阻止生成拷贝操作;声明拷贝操作,则会阻止生成移动操作(见示例代码3)
代码语言:javascript
复制
//////////////////  示例代码1 begin /////////////////////
#include<iostream>

class NullClass {};

void test_null()
{
  NullClass a, b;

  a = b;
  NullClass c = a;

  NullClass d = std::move(a);
  d = std::move(b);
}

//编译器生成的函数如下
class NullClass
{
  public: 
  // inline constexpr NullClass() noexcept = default;
  // inline constexpr NullClass(const NullClass &) noexcept = default;
  // inline constexpr NullClass(NullClass &&) noexcept = default;
  // inline NullClass & operator=(const NullClass &) noexcept = default;
  // inline NullClass & operator=(NullClass &&) noexcept = default;
};
////////////////////  示例代码1 end /////////////////////

//////////////////  示例代码2 begin /////////////////////
class CopyClass {
public:
  CopyClass()=default;
  CopyClass(const CopyClass& rhs){}
};

void test_copy()
{
  CopyClass a, b;

  a = b;
  CopyClass c = a;

  CopyClass d = std::move(a);
  d = std::move(b);
}
//编译器生成的函数如下:
class CopyClass
{
  
  public: 
  inline constexpr CopyClass() noexcept = default;
  inline CopyClass(const CopyClass & rhs){
  }
  
  // inline CopyClass & operator=(const CopyClass &) noexcept = default;
};
////////////////////  示例代码2 end /////////////////////


//////////////////  示例代码3 begin /////////////////////
class MoveClass {
public:
  MoveClass()=default;
  MoveClass(const MoveClass&& rhs){}
};

void test_move()
{
  MoveClass a, b;

  //a = b;//声明移动操作,禁止了拷贝赋值
  //MoveClass c = a;/声明移动操作,禁止了拷贝构造

  MoveClass d = std::move(a);
  //d = std::move(b);/声明移动构造,禁止了移动赋值
}
//编译器生成的函数如下:
class MoveClass
{
  
  public: 
  inline constexpr MoveClass() noexcept = default;
  inline MoveClass(const MoveClass && rhs){
  }
  
  // inline constexpr MoveClass(const MoveClass &) /* noexcept */ = delete;
  // inline MoveClass & operator=(const MoveClass &) /* noexcept */ = delete;
};

////////////////////  示例代码3 end /////////////////////

Rule of Three

Rule of Three讲,如果类声明了拷贝构造函数、拷贝赋值运算符、析构函数中的任何一个,就得同时声明这三个。默认理解为声明这三个中的一个必定涉及到了资源管理,所以默认的拷贝操作也就不再适宜,所以均需要用户自定义。

C++11 标准指出,存在拷贝操作或析构函数的条件下,仍然自动生成拷贝操作是废弃行为(见如下示例代码)。虽然当前的编译器仍然支持自动生成另一个拷贝操作,但强烈建议遵守大三律,程序员同时显示声明这三个函数。

代码语言:javascript
复制
class CopyClass {
public:
  CopyClass() = default;
  ~CopyClass()=default;
  CopyClass(const CopyClass& rhs) {}
};

void test_copy()
{
  CopyClass a, b;

  a = b;
  CopyClass c = a;

  CopyClass d = std::move(a);
  d = std::move(b);
}
//编译器生成的函数如下:
class CopyClass
{
  
  public: 
  inline constexpr CopyClass() noexcept = default;
  inline ~CopyClass() /* noexcept */ = default;
  inline CopyClass(const CopyClass & rhs){
  }
  
  // inline constexpr CopyClass & operator=(const CopyClass &) noexcept = default;
};

Rule of Five

Rule of Five讲,如果类声明了拷贝构造函数、拷贝赋值运算符、析构函数、移动构造函数、移动赋值运算符中的任何一个,就得同时声明这五个。结合Rule of Three很容易理解。

如果声明了拷贝构造函数、拷贝赋值运算符、析构函数中的任何一个,必须同时声明这三个。

如果声明了移动操作中的一个则会抑制另一个的生成,因此,需要同时声明他们两个。

如果声明了拷贝操作则会抑制移动操作的生成,所以声明了拷贝操作,则必须声明移动操作。反之亦然。

同时生成这五个函数的代码示例如下:

代码语言:javascript
复制
class CopyFive {
public:
  CopyFive() = default;
  ~CopyFive()=default;
  CopyFive(const CopyFive& rhs) =default;
  CopyFive(CopyFive&& rhs) = default;

  CopyFive& operator=(const CopyFive& rhs) = default;
  CopyFive& operator=(CopyFive&& rhs) = default;
};

总结

本文结合C++特种成员函数的生成限制,讲解了Rule of Three和Rule of Five,建议我们都遵守Rule of Five,同时声明这5个函数。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-05-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员的园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档