返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++类中隐藏的默认函数有哪些
  • 573
分享到

C++类中隐藏的默认函数有哪些

2023-06-29 10:06:38 573人浏览 泡泡鱼
摘要

这篇文章主要介绍了c++类中隐藏的默认函数有哪些,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Test类中隐藏的六个默认的函数class Test{public:/

这篇文章主要介绍了c++类中隐藏的默认函数有哪些,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

Test类中隐藏的六个默认的函数

class Test{public://默认的构造函数Test();//析构函数 ~Test();//拷贝构造函数 Test(const Test &t);//赋值函数 Test& operator=(const Test &x);//一般对象取地址函数 Test* operator&();//常对象取地址函数 const Test* operator&()const;private:int data;//int *data;// 注意:如果成员中含有指针类型,需重载拷贝函数与赋值函数// 否则会造成浅拷贝// 另外,需要注意在析构函数中,释放类中使用的额外资源(堆区申请的资源)};

1.构造函数

作用:对象所在的内存空间做初始化 、给对象赋资源

特点

可以重载 :可以根据实际需要进行缺省的、多参重载

不依赖对象:对象无法调用构造函数,只能在对象定义点被调用

//成员函数类外实现,需在函数名前指定作用域,否则编译器会认为在定义一个普通的函数Test::Test()//类中默认的构造函数{}//此外,构造函数可以支持重载,我们可以根据需要自己写一些构造函数//需要注意的是,如果我们自己写了构造函数,那么编译器就不会提供默认的构造函数了Test::Test(int d = 0 )//缺省的构造函数{data = d;}Test::Test(int d = 0 ):data(d)//缺省的构造函数,用初始化列表的方式初始化{}

两者初始化的区别在于,初始化列表是真正意义上的初始化,它告诉编译器在实例化对象的时候以何种方式对成员赋值,而在前者的赋值规则写在了构造函数内部,是在已经生成了成员变量之后再进行的赋值操作。

初始化列表示例

Tips: 注意区分列表参数初始化列表初始化的区别 。列表参数初始化即在函数的形参列表后通过 fun(int ia) :mval(ia) 冒号+括号的这种方式初始化,而列表初始化一般是指如 std::vector<int> vec{ 1,2,3,4,5 }; vec{1,2,3,4,5}; 这种,在定义时通过 { } 括起来的列表初始化“数组”的行为。 事实上,在C++11标准中还有一种就地初始化的概念,这里先不做讨论。

对于 初始化列表 有几点特性需要注意:

比如以下操作,成员变量有引用类型和const类型,在C++中规定const类型为一个常量,定义时必须初始化,而引用我们认为是一个变量的别名也需要在定义时就初始化。所以以下操作只能使用初始化列表的方式初始化。

class Test{public:Test(int a, int b, int c):ma(a),mb(b),mc(c){}private:int ma;int& mb;const int mc;};

此外,如果有多个成员变量需要使用初始化列表的方式初始化,需要注意一点细节,初始化的顺序只与成员变量的定义顺序相关

如以下程序,可以写成Test(int a):ma(mb), mb(a){}Test(int a):mb(a),ma(mb){}因为成员变量的定义顺序为int mb; int ma;,也就是说赋值顺序与初始化列表无关,只与成员变量被定义的顺序有关。

class Test{public:Test(int a):ma(mb), mb(a)//mb先被定义出来,先给mb赋值,再给ma赋值{}Test(int a) :mb(ma), ma(a){}public:void Show(){std::cout << "ma: " << ma << std::endl;std::cout << "mb: " << mb << std::endl;}private:int mb;int ma;};

注:以下函数的Test类成员均为 int *ma ,表示数据成员为指针时,各成员函数的实现方法。

2.析构函数

作用:释放对象所占的其他资源。

特点

不可重载 : 对象销毁时会调用析构函数,并释放空间。依赖对象:可手动调用即this->~Test()或 Test t; t.~Test(),但是不建议,因为对象销毁时会自动调用,如果手动调用可能会引起内存空间的重复析构导致程序崩溃

//默认的析构函数 Test::~Test(){//没有额外的资源,什么都不写}
//如果程序中有额外的空间需要释放class Test{public://构造函数 Test(int ia = 0) {data = new int{ ia };//data指向一块堆区内存}//析构函数~Test();private:int* data;};//析构函数 Test::~Test(){delete data;//把额外空间的释放写进析构函数data = nullptr;}

3.拷贝构造函数

作用:拿一个已存在的对象来生成相同类型的新对象

注意:类中提供的拷贝构造函数为一个浅拷贝类型,即如果成员变量中含有指针类型,它在进行拷贝构造的时候不会进行额外空间的开辟,最终会造成函数析构时的错误。

class Test{public://构造函数 Test(int ia = 0) {data = new int{ ia };//data指向一块堆区内存}//拷贝构造函数 Test(const Test &t);//一定要传引用,否则在开辟形参的过程中会递归的调用拷贝构造函数来构造形参,而函数始终无法执行private:int* data;};//默认的拷贝构造函数 Test::Test(const Test &t){data = t.data;//浅拷贝,只把现有的成员变量进行拷贝,没有对堆区内存进行拷贝,使多个对象的data指向了同一片堆区空间,在对象销毁时会造成空间的重复释放引发程序崩溃。}//拷贝构造函数Test::Test(const Test &t){data = new int;//如果是字符类型data = new char[strlen(t.data) + 1]; // 注意strlen() 函数不能传递nullptr参数strcpy_s(data,sizeof(int), t.data);}// 或者使用初始化列表的方式Test::Test(const Test& t) :data(new int{*(t.data)}){}

4.赋值运算符的重载函数

作用:拿一个已存在的对象给相同类型的已存在对象赋值

实现步骤

赋值判断

释放旧资源

生成新资源

赋值

class Test{public://构造函数 Test(int ia = 0) {data = new int{ ia };//data指向一块堆区内存}//赋值函数Test& operator=(const Test &x);//以自身类类型的引用的方式返回private:int* data;};//默认的赋值函数(浅拷贝)Test& Test::operator=(const Test &x){if(this!=&x)//自赋值判断{data=x.data;//浅拷贝}return *this;//返回自身类类型的引用}//赋值函数(深拷贝)Test& Test::operator=(const Test &x){if(this!=&x)//自赋值判断{delete data;//释放原资源 //delete[] data; 如果申请的空间是多个,即数组形式,需要delete [] data 释放data = new int;//开辟空间memcpy(data, x.data, sizeof(data));// 赋值}return *this;//返回自身类类型的引用}

5.一般对象取地址函数

//一般对象取地址函数 Test::Test* operator&(){return this;}

常对象取地址函数

//常对象取地址函数 const Test::Test* operator&()const{return this;}

C++11以后增加了右值引用的概念,同时增加了移动构造函数、和移动赋值函数

7.移动构造函数

作用:针对某些情况构造对象的优化,避免重复的开辟内存。

使用场景:比如把临时对象的资源作为构建新对象的资源使用,而临时对象销毁时,资源继续被其他对象使用(这里就节省了一次旧对象资源的的销毁与新对象资源申请的开销)。

class Test{public://构造函数 Test(int ia = 0) :data(new int{ ia }){}// 拷贝构造 ..// 赋值..// 析构..// 移动构造Test(Test&& rhs){this->data = rhs.data;// 资源的转移rhs.data = nullptr;// 资源的释放} public:void print() { std::cout << (void*)data << std::endl; }private:int* data;};int main(){Test t(10); t.print();Test t2(std::move(t));// 把 t 的资源转移给 t2t.print();t2.print();return 0;}

8.移动赋值函数

作用:资源的转移,针对某些情况下,节省内存的开辟。

class Test{public://构造函数 Test(int ia = 0) :data(new int{ ia }){}// 拷贝构造 ..// 赋值..// 析构..// 移动构造// 移动赋值Test& operator=(Test&& rhs) noexcept // 不抛出异常{if (this != &rhs)// 防止自赋值{delete data;// 销毁当前资源this->data = rhs.data;// 转移资源,即接收对方资源rhs.data = nullptr;// 对方放弃资源的拥有}return *this;}public:void print() { std::cout << (void*)data << std::endl; }private:int* data;};int main(){Test t(10), t2(20);t.print();t2.print();t2 = std::move(t);// 把 t 的资源交给 t2t.print();// 输出 00000000t2.print();return 0;}

补充:

另外一个关于移动构造的话题是异常。对于移动构造函数来说,抛出异常有时是件危险的事情。因为可能移动语义还没完成,一个异常却抛出来了,这就会导致一些指针就成为悬挂指针。因此程序员应该尽量编写不抛出异常的移动构造函数,通过为其添加一个noexcept关键字,可以保证移动构造函数中抛出来的异常会直接调用terminate程序终止运行,而不是造成指针悬挂的状态。而标准库中,我们还可以用一个std::move_if_noexcept的模板函数替代move函数。该函数在类的移动构造函数没有noexcept关键字修饰时返回一个左值引用从而使变量可以使用拷贝语义,而在类的移动构造函数有noexcept关键字时,返回一个右值引用,从而使变量可以使用移动语义。

关于移动构造函数的示例程序,引用自《深入理解C++11》一书:

#include <iOStream>using namespace std;class HasPtrMem {public:HasPtrMem() :d(new int(3)) {cout<<"Construct:"<<++n_cstr<<endl;}HasPtrMem(const HasPtrMem& h) :d(new int(*h.d)) {cout<<"Copy construct:"<<++n_cptr<<endl;}HasPtrMem(HasPtrMem&& h) :d(h.d) {//移动构造函数h.d = nullptr;//将临时值的指针成员置空cout<<"Move construct:"<<++n_mvtr<<endl;}~HasPtrMem() {delete d;cout<<"Destruct:"<<++n_dstr<<endl;}int* d;static int n_cstr;static int n_dstr;static int n_cptr;static int n_mvtr;};int HasPtrMem::n_cstr = 0;int HasPtrMem::n_dstr = 0;int HasPtrMem::n_cptr = 0;int HasPtrMem::n_mvtr = 0;HasPtrMem GetTemp() {HasPtrMem h;cout<<"Resource from"<<__func__<<":"<<hex<<h.d<<endl;return h;}int main() {HasPtrMem a = GetTemp();cout<<"Resource from"<<__func__<<":"<<hex<<a.d<<endl;}//编译选项:g++ -std=c++11 test.cpp -fno-elide-constructors

输出:左边是输出结果,右边是注释

Construct:1// 在GetTemp() 函数中,执行 HasPtrMem h; 构造对象Resource fromGetTemp:0x1047f28// 在GetTemp() 函数中,执行cout << ... Move construct:1// 在GetTemp() 函数中,return h; 产生临时对象,此时第一次调用移动构造Destruct:1// 进入main() 函数时,GetTemp();调用结束、进行清栈(栈帧回退),析构掉局部对象( h )Move construct:2// 在main() 函数中,执行 GetTemp(); 后产生的返回值是一个临时无名对象,调用了移动构造函数,此时第二次调用移动构造Destruct:2// 在main() 函数中,执行了a = GetTemp(); 后,临时对象生存期结束,析构掉临时对象(函数返回值)Resource frommain:0x1047f28// 在main() 函数中,执行cout << ... Destruct:3// main() 函数结束,对象 a 生存周期结束,销毁对象 a

需要注意的是,在编译器中存在被称为RVO/NRVO的优化(RVO,Return Value Optimization,返回值优化,或者NRVO,Named Return Value optimization)。因此在上述编译时使用了 -fno-elide-constructors 选项在g++/clang++中关闭这个优化,这样可以使我们在代码运行的结果中较为容易地利用函数返回的临时量右值。 

如果在编译的时候不使用该选项的话,我们写的很多含有移动语义的函数都被省略了。例如以下的代码

A ReturnRvalue(){A a();return a;}A b=ReturnRvalue();

b 变量实际就使用了ReturnRvalue函数中a的地址,任何的拷贝和移动都没有了。通俗地说,就是b变量直接“霸占”了a变量。这是编译器中一个效果非常好的一个优化。不过RVO/NRVO并不是对任何情况都有效。比如有些情况下,一些构造是无法省略的。还有一些情况,即使RVO/NRVO完成了,也不能达到最好的效果。 

总体而言,移动语义除了可以解决某些情况下编译器无法解决的优化问题,还在一些其他特殊的场合有着重要的用途(比如在unique_ptr中禁止构造函数,却可以通过移动的构造或移动赋值对unique_ptr拥有的资源进行转移)。

感谢你能够认真阅读完这篇文章,希望小编分享的“C++类中隐藏的默认函数有哪些”这篇文章对大家有帮助,同时也希望大家多多支持编程网,关注编程网其他教程频道,更多相关知识等着你来学习!

--结束END--

本文标题: C++类中隐藏的默认函数有哪些

本文链接: https://lsjlt.com/news/324217.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C++类中隐藏的默认函数有哪些
    这篇文章主要介绍了C++类中隐藏的默认函数有哪些,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Test类中隐藏的六个默认的函数class Test{public:/...
    99+
    2023-06-29
  • C++类中隐藏的几个默认函数你知道吗
    目录1.构造函数2.析构函数3.拷贝构造函数4.赋值运算符的重载函数5.一般对象取地址函数7.移动构造函数8.移动赋值函数补充:总结Test类中隐藏的六个默认的函数 class Te...
    99+
    2024-04-02
  • C++类的默认成员函数有哪些
    本文小编为大家详细介绍“C++类的默认成员函数有哪些”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++类的默认成员函数有哪些”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。类的默认成员函数每个类中,如果不主动实...
    99+
    2023-06-29
  • C++函数的默认参数有哪些
    本篇内容介绍了“C++函数的默认参数有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!C++当中的支持默认参数,如果你学过Python,那...
    99+
    2023-06-25
  • 【C++类和对象】类有哪些默认成员函数呢?(下)
    文章目录 一、类的6个默认成员函数二、日期类的实现2.1 运算符重载部分2.2 日期之间的运算2.3 整体代码1.Date.h部分2. Date.cpp部分 三. const成员函数...
    99+
    2023-09-06
    c++ 开发语言
  • C++中类的默认成员函数详解
    目录一、构造函数二、析构函数三、拷贝构造函数四、赋值函数(赋值运算符重载)总结C++中,对于任意一个类,都会为我们提供4个默认的成员函数(如果我们不显示的去声明)—&md...
    99+
    2024-04-02
  • C++中类的默认成员函数怎么用
    这篇文章主要介绍了C++中类的默认成员函数怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。C++中,对于任意一个类,都会为我们提供4个默认的成员函数(如果我们不显示的去声...
    99+
    2023-06-29
  • C++类中六个默认的成员函数详解
    目录浅谈构造函数析构函数拷贝构造函数赋值重载函数const成员函数取地址及const取地址重载函数深挖 构造函数拷贝构造函数赋值运算符重载函数总结浅谈 先来说一下“this...
    99+
    2024-04-02
  • C++类中的六大默认成员函数详解
    在C++中,当你去创建一个类的时候,即便这个类是空类,也会自动生成下面6个默认成员函数,在本篇博客中,我将逐一分析下面6个默认成员函数。 构造函数 构造函数并不是去构造函数的函数,...
    99+
    2024-04-02
  • 详解C++中类的六大默认成员函数
    目录一、类的默认成员函数二、构造函数Date(形参列表)1、构造函数的函数名和返回值2、构造函数的调用3、构造函数的重载4、系统生成的默认构造函数5、系统生成的默认构造函数的作用6、...
    99+
    2022-11-13
    C++类默认成员函数 C++类六大默认成员函数 C++类成员函数
  • Python 函数默认返回None原因有哪些
    小编给大家分享一下Python 函数默认返回None原因有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Python 有一项默认的做法,很多编程语言都没有,所...
    99+
    2023-06-16
  • CSS3中的隐藏特性有哪些
    这篇文章主要介绍“CSS3中的隐藏特性有哪些”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“CSS3中的隐藏特性有哪些”文章能帮助大家解决问题。 CSS3为web设...
    99+
    2024-04-02
  • Python中的隐藏彩蛋有哪些
    本篇内容介绍了“Python中的隐藏彩蛋有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、使用re.DEBUG查看正则表达式的匹配过程...
    99+
    2023-06-17
  • Python中的隐藏技巧有哪些
    这篇文章主要介绍了Python中的隐藏技巧有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Python中的隐藏技巧有哪些文章都会有所收获,下面我们一起来看看吧。1、功能属性这种 hack 类似于类和对象概念...
    99+
    2023-07-05
  • css中隐藏命令有哪些
    这篇“css中隐藏命令有哪些”文章,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要参考一下,对于“css中隐藏命令有哪些”,小编整理了以下知识点,请大家跟着小编的步伐一步一步的慢慢理解,接下来就让我们进入主题吧。cs...
    99+
    2023-06-06
  • C++函数的默认参数详情
    文章转自微信 公众号:Coder梁(ID:Coder_LT) C++当中的支持默认参数,如果你学过Python,那么想必对此不会陌生。C++中的默认参数的用法和Python基本一致...
    99+
    2024-04-02
  • C++类包含函数有哪些
    这篇文章主要讲解了“C++类包含函数有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++类包含函数有哪些”吧!C++类包含三个我们关心的函数:构造函数,析构函数,和所有重要的 DoSo...
    99+
    2023-06-17
  • C++ 函数中默认参数的注意事项
    c++++ 函数中默认参数需要注意:必须出现在参数列表末尾。不可为同一参数指定多个默认值。vararg 可变数量参数不可拥有默认值。默认参数不可被重载函数的参数共享。 C++ 函数中默...
    99+
    2024-04-20
    函数 c++ 默认参数
  • C++ 中函数的返回值类型有哪些?
    c++++ 中函数的返回值类型定义了执行后返回的值的类型:基本类型:void(不返回值)、bool、整数、浮点、字符引用类型:类型引用、类型指针结构体或类:类型实例 C++ 中函数的返...
    99+
    2024-04-12
    c++ 返回值类型
  • Python隐藏的特性有哪些
    本篇内容主要讲解“Python隐藏的特性有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python隐藏的特性有哪些”吧!1...是的,你没看错,在Python中...是一个有效的构造。.....
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作