C++类和对象:补充拷贝构造

2023-11-17

前言:如果一个类中什么成员都没有,简称为空类。空类中什么都没有吗?并不是的,任何一个类在我们不写的情况下,都会自动生成下面6个默认成员函数。


目录

一、六大函数

1、构造函数

❤1.定义

❤2.特性

❤3.赋值

❤4.初始化列表

2、拷贝构造函数 (❗❗❗❗❗❗)

3、析构函数

1.概念

2.特性

4、运算符重载

5、取地址及const取地址操作符重载

二、const成员


一、六大函数

1、构造函数

❤1.定义

构造函数 是一个 特殊的成员函数,名字与类名相同 , 创建类类型对象时由编译器自动调用 ,保证每个数据成员都有 一个合适的初始值,并且在对象的生命周期内只调用一次
class Date{ 
public:
 void SetDate(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
private:
 int _year;
 int _month;
 int _day;
};

构造函数大致如上代码中的SetDate方法。

❤2.特性

构造函数 是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象
其特征如下:
1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器 自动调用 对应的构造函数。
4. 构造函数可以重载。
5.如果类中没有显式定义构造函数,则 C++ 编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
6. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个(对于一个对象来说,在创造一个对象只能调用一个默认构造函数)。
注:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。
class Date
{
public :
 // 1.无参构造函数
 Date ()
 {}
 
 // 2.带参构造函数
 Date (int year, int month , int day )
 {
 _year = year ;
 _month = month ;
 _day = day ;
 }
private :
 int _year ;
 int _month ;
 int _day ;
};
void TestDate()
{ 
 Date d1;              // 调用无参构造函数
 Date d2 (2015, 1, 1); // 调用带参的构造函数
 
 // 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
 // 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
 Date d3(); 
}

 此处编译器认为d3是一个声明。

在我们不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d1 对象调用了编译器生成的默认构造函数,但是d1 对象 year/ month/_day ,依旧是随机值。也就说在这里 编译器生成的默认构造函数并没有什么卵 用??

7.编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数

借以下实例可以理解为:在Date类实例化一个对象时,调用的编译器生成的构造函数能够调用自定义类型Time的构造函数。

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

C++ 把类型分成内置类型 ( 基本类型 ) 和自定义类型。内置类型就是语法已经定义好的类型:如
int/char... ,自定义类型就是我们使用 class/struct/union 自己定义的类型

 8.成员变量的命名风格

 这里year是一个随机值,看得出来编译器误认year为构造函数的形参

// 所以我们一般都建议这样
class Date
{
public:
 Date(int year)
 {
 _year = year;
 }
private:
 int _year;
};
// 或者这样。
class Date
{
public:
 Date(int year)
 {
 m_year = year;
 }
private:
 int m_year;
};

❤3.赋值

在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。
class Date
{
public:
 Date(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 
private:
 int _year;
 int _month;
 int _day;
};

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称作为类对象成员的初始化,构造函数体中的语句只能将其称作为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值

❤4.初始化列表

可结合特性来理解,该方法实质上类似于特性代码例块的带参形式

class Date
{
public:
 Date(int year, int month, int day)
 : _year(year), _month(month), _day(day)
 {}
 
private:
 int _year;
 int _month;
 int _day;
};
【注意】
1. 每个成员变量在初始化列表中 只能出现一次 ( 初始化只能初始化一次 )
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    引用成员变量
    const 成员变量
    自定义类型成员 ( 该类没有默认构造函数 )
class A
{
public:
 A(int a)  //此处均可
 :_a(a)
 {}
private:
 int _a;
};
class B
{
public:
 B(int a, int ref)
 :_aobj(a)
 ,_ref(ref)
 ,_n(10)
 {}
private:
 A _aobj; // 没有默认构造函数
 int& _ref; // 引用
 const int _n; // const 
};
3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
class Date
{
public:
 Time(int hour = 0)
 :_hour(hour)
 {
 cout << "Time()" << endl;
 }
private:
 int _hour;
};
class Date
{
public:
 Date(int day)
 {}
private:
 int _day;
 Time _t;
};
int main()
{
 Date d(1);
}
4. 成员变量 在类中 声明次序 就是其在初始化列表中的 初始化顺序 ,与其在初始化列表中的先后次序无关。
class A
{
public:
	A(int a)
		:_a1(a)
		, _a2(_a1)
	{}

	void Print() {
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2;
	int _a1;
};
void main() {
	A aa(1);
	aa.Print();
}

由于a还未初始化是一个随机值,而成员变量在类中声明次序就是其在初始化列表中的初始化顺序,导致_a2是一个随机值。

2、拷贝构造函数 (❗❗❗❗❗❗)

      (很重要哟)

通过对类的学习,我们不难发现,一个对象往往含有多个成员属性,对于不同的对象我们也会想让他们像参数给参数赋值这种形式,完成对象之间的复制粘贴,这时拷贝构造函数的出现完美解决了这个问题

1.定义

拷贝构造函数 只有单个形参 ,该形参是对本 类类型对象的引用 ( 一般常用 const 修饰 ) ,在用 已存在的类类型对象 创建新对象时由编译器自动调用。

2.特征

拷贝构造函数也是特殊的成员函数,其 特征 如下:
  1. 拷贝构造函数 是构造函数的一个重载形式
  2. 拷贝构造函数的 参数只有一个 必须使用引用传参 ,使用 传值方式会引发无穷递归调用
class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 Date(const Date& d)
 {
_year = d._year;
 _month = d._month;
 _day = d._day;
 }
private:
 int _year;
 int _month;
 int _day;
};
int main()
{
 Date d1;
 Date d2(d1);
 return 0;
}

假如不使用引用:

逻辑梳理:

在创建d2这个对象时,要先用d1来拷贝构造对象d,对于对象d,他要动用拷贝构造函数Date(const Date d),这里就会陷入逻辑循环:就是我要通过a来创造b,那我就要先通过a来创造b1,可是要创造我的b1就要先创造出我b1中的b2,然后再去创造b2,但是b2中还有b3,就一直这样递归走下去,直至栈溢出和程序崩溃。

 3. 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。
上述刻非书面理解为系统生成的默认拷贝构造函数只是针对成员进行拷贝,A对象有几个成员,则B对象同样有几个成员。
class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
private:
 int _year;
 int _month;
 int _day;
};
int main()
{
 Date d1;
// 这里d2调用的默认拷贝构造完成拷贝,d2和d1的值也是一样的。
 Date d2(d1);
 return 0;
}

4.对于大部分普通的类编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,但是一部分类仍需我们自己定义拷贝构造函数。

class String
{
public:
 String(const char* str = "jack")
 {
 _str = (char*)malloc(strlen(str) + 1);
 strcpy(_str, str);
 }
 ~String()
 {
 cout << "~String()" << endl;
 free(_str);
 }
private:
 char* _str;
};
int main()
{
 String s1("hello");
 String s2(s1);
}

3、析构函数

既然我们创造了一个对象,那我们怎么让这个对象消失呢?

1.概念

析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而 对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。

2.特性

析构函数 是特殊的成员函数。
特征 如下:
1. 析构函数名是在类名前加上字符 ~
2. 无参数无返回值。
3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
4. 对象生命周期结束时, C++ 编译系统系统自动调用析构函数。
typedef int DataType;
class SeqList
{ 
public :
 SeqList (int capacity = 10)
 {
 _pData = (DataType*)malloc(capacity * sizeof(DataType));
 assert(_pData);
 
 _size = 0;
 _capacity = capacity;
}
 
 ~SeqList()
 {
 if (_pData)
 {
 free(_pData ); // 释放堆上的空间
 _pData = NULL; // 将指针置为空
 _capacity = 0;
 _size = 0;
 }
 }
 
private :
 int* _pData ;
 size_t _size;
 size_t _capacity;
};
关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对会自定类型成员调用它的析构函数。
class String
{
public:
 String(const char* str = "jack")
 {
 _str = (char*)malloc(strlen(str) + 1);
 strcpy(_str, str);
 }
 ~String()
 {
 cout << "~String()" << endl;
 free(_str);
 }
private:
 char* _str;
};
class Person
{
private:
 String _name;
 int _age;
};
int main()
{
 Person p;
 return 0;
}

4、运算符重载

C++ 为了增强代码的可读性引入了运算符重载 运算符重载是具有特殊函数名的函数 ,也具有其返回值类
型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字 operator 后面接需要重载的运算符符号
函数原型: 返回值类型  operator 操作符 ( 参数列表 )
注意:
   
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++类和对象:补充拷贝构造 的相关文章

随机推荐

  • 2023届计算机专业弄潮儿如何快速找毕业论文文献?

    人生苦短 我用Python 一 准备工作 软件选择 Python3 8 pycharm 模块 requests 模拟请求 Selenium 浏览器自动化操作 win r打开搜索框 输入cmd按确定打开命令提示符窗口 输入pip instal
  • 孔乙己 长衫

    学历是 敲门砖 or 枷锁 孔乙已是鲁迅笔下人物 穷困流倒还穿着象征读书人的长衫 迁腐 麻木 最近 大家自我调佩是 当代孔乙己 学历成为思想负担 找工作时高不成低不就 你可以从以下几个角度说说你对看法 一 社会对于学历和职业之间的关系认知是
  • MAC 命令行拷贝文件夹

    命令 cp R 源文件 目标文件 cp R libsvm 3 23 Applications MATLAB R2017b app toolbox 将当前目录下的libsvm 3 23拷贝到 Applications MATLAB R2017
  • 利用xpath爬取网页

    xpath应该是爬取网页最简单的方法啦 因为你需要要懂xpath 可以直接通过浏览器来获取你想要的内容 以Chrome为例 按f12检查网页 用箭头点击自己想要的地方 比如我想提取出 故宫博物院 的xpath地址 右击 点击copy 然后选
  • PIE-engine 下载MODIS Mod09A1,去云并年度平均值合成

    最近毕设需要下载数据 本来懒想买现成的 发现价格实在离谱 考虑了GEE 梯子钱也不想花 一怒之下学了PIE 改的示例 没啥技术含量 var featureCollection0 pie FeatureCollection user 1506
  • 杭电 oj 2010 水仙花数 C++

    Problem Description 春天是鲜花的季节 水仙花就是其中最迷人的代表 数学上有个水仙花数 他是这样定义的 水仙花数 是指一个三位数 它的各位数字的立方和等于其本身 比如 153 1 3 5 3 3 3 现在要求输出所有在m和
  • 瀑布流布局 (移动端多数用的比较多 直播软件 浏览图片)

    瀑布流布局的核心是基于一个网格的布局 而且每行包含的项目列表高度是随机的 随着自己内容动态变化高度 同时每个项目列表呈堆栈形式排列 最为关键的是 堆栈之间彼此之间没有多余的间距差存大 场景 视频图片封面因高度不同 展示 案例效果 直播软件
  • vue的package.json中dependencies和devDependencies区别

    1 dependencies 应用能够正常运行所依赖的包 这种 dependencies 是最常见的 用户在使用 npm install 安装你的包时会自动安装这些依赖 2 devDependencies 开发应用时所依赖的工具包 通常是一
  • 【解决方案】LeetCode中的Monaco编辑器无法加载

    在Edge浏览器中经常出现LeetCode网站中的Monaco编辑器加载不出来的情况 而Codemirror编辑器又不是很好用 所以特此记录一下这个问题的解决方案 解决方法 打开Edge的设置 进入 隐私 搜索和服务 关掉 增强Web安全性
  • 13-FreeRTOS任务创建与删除

    任务创建和删除API函数位于文件task c中 需要包含task h头文件 task h里面包函数任务的类型函数 例如 对xTaskCreate的调用 通过指针方式 返回一个TaskHandle t 变量 然后可将该变量用vTaskDele
  • 模板的特化和萃取

    之前对模板化编程进行了总结 详见https blog csdn net timecur article details 89949643 这篇将介绍模板的重要概念 模板特化 模板的特化 模板针对某些具体的类型不能处理或者处理结果有误 就需要
  • vue项目开发流程

    1 创建项目 1 首先创建项目 我用的项目是vue 3 0 可以在新建文件夹中cmd 进入命令符 vue create 项目名 创建项目也可以在 命令符中vue ui 在浏览器中创建项目 2 项目安装好后 安装自己需要的各种插件 3 我们常
  • 【C语言技巧】STM32实现 printf 打印语句

    包含头文件 include
  • 计算机英语 st,1st、2nd、3rd、…10th都是什么的缩写?怎么读?10th之...-1st-英语-司俜辰同学...

    概述 本道作业题是司俜辰同学的课后练习 分享的知识点是1st 指导老师为任老师 涉及到的知识点涵盖 1st 2nd 3rd 10th都是什么的缩写 怎么读 10th之 1st 英语 下面是司俜辰作业题的详细 题目 1st 2nd 3rd 1
  • mysql的高级查询实例_MySQL高级查询---连接查询实例

    MySQL高级查询 连接查询实例 使用sql查询很简单 很基础的SQLECT语句查询 如果想从多个表查询比较复杂的信息 就会使用高级查询实现 常见的高级查询包括多连接查询 外连接查询与组合查询等 今天我先学习最常用的连接查询 我先以一张pe
  • 编译qt5.9-arm-qmake

    一 arm gcc环境配置 tar xvf rock3288 kernel arm linux gcc C opt vim basgrc 在最后面添加 export PATH opt gcc linaro arm linux gnueabi
  • Android EditText文本改变监听和获取到焦点的监听

    开发app快两年了 总结了一些小知识 以前没时间发表 最近有时间了 和大家分享一下 别忘记初始化 EditText edtUserName 添加文本改变的监听 edtUserName addTextChangedListener new T
  • VSCode离线汉化教程

    VSCode汉化包下载路径 https marketplace visualstudio com items itemName MS CEINTL vscode language pack zh hans 选择 Version Histor
  • 代码丢了不要怕,有jar包就能反编译找回

    推荐一个好用的反编译工具 直接上下载地址 http jd benow ca 根据自己的电脑下载版本 我下载的是windows版本 压缩包解压运行 打开jar包找到你的代码 注意 如果jar包也没有的就想想该重写了
  • C++类和对象:补充拷贝构造

    前言 如果一个类中什么成员都没有 简称为空类 空类中什么都没有吗 并不是的 任何一个类在我们不写的情况下 都会自动生成下面6个默认成员函数 目录 一 六大函数 1 构造函数 1 定义 2 特性 3 赋值 4 初始化列表 2 拷贝构造函数 3
Powered by Hwhale