C++类与对象-六大成员函数

news2024/9/9 0:15:02

默认成员函数就是用户没有显式实现,编译器会⾃动⽣成的成员函数称为默认成员函数。⼀个空类编译器会默认⽣成8个默认成员函数。本文只介绍其中6个,C++11增加两个函数见后续博客。

目录

一、构造函数

1.1 概念

1.2 特性

1.3 使用举例

1.4 初始化列表

1.4.1 概念

1.4.2 特性

1.4.3 使用

二、析构函数

2.1 概念

2.2 特性

2.3 使用

三、拷贝构造

3.1 概念

3.2 特性

3.3 使用

四、赋值运算符重载

4.1 特性

4.2 使用

4.3 与拷贝构造的区别

五、取地址操作符重载

5.1 cosnt

5.2 重载

六、操作符重载补充

6.1 前置++与后置++

6.2 流操作符重载


一、构造函数

1.1 概念

构造函数是一个特殊的成员函数,名字与类名相同,不写返回值。创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次。

1.2 特性

  1. 函数名与类名相同。⽆返回值。 (返回值啥都不需要给,也不需要写void)
  2. 构造函数可以重载。分为无参构造函数,带参构造函数,全缺省构造函数
    class Date
    {
    public:
    	// 1. ⽆参构造函数
    	Date()
    	{
    		_year = 1;
    		_month = 1;
    		_day = 1;
    	}
    	// 2. 带参构造函数
    	Date(int year, int month, int day)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	 //3. 全缺省构造函数
    	Date(int year = 1, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
  3. 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦用户显式定义编译器将不再⽣成。
  4. 默认构造函数:⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数。这三个函数有且只有⼀个存在,不能同时存在。不传实参就可以调⽤的构造就叫默认构造。
  5. 用户不写,编译器默认⽣成的构造,对内置类型成员变量的初始化是不确定的,看编译器。对于⾃定义类型成员变量,要求调⽤这个成员变量的默认构造函数初始化。如果这个成员变量,没有默认构造函数,那么就会报错

1.3 使用举例

#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1; // 调⽤默认构造函数
	Date d2(2005, 6, 8); // 调⽤带参的构造函数
	d1.Print();
	d2.Print();
	return 0;
}

1.4 初始化列表

1.4.1 概念

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟
一个放在括号中的初始值表达式

class Date
{
public:
Date(int year, int month, int day)
    : _year(year)
    , _month(month)
    , _day(1)
{}
private:
    int _year;
    int _month;
    int _day;
};

 如何理解初始化列表?

借助C语言的特性:在private中可以看作是变量的声明,如果用之前在函数体中的构造,我们可以发现缺少了变量的定义。事实上如果我们不写初始化列表,编译器会自动生成初始化列表。

由此可知,初始化列表其实是每个成员变量定义初始化的地⽅

为什么会有初始化列表?

有些变量比如:const变量,还有引用----要求在定义时初始化,仅仅依靠之前在函数体内初始化的方式是无法对这些变量进行初始化的,所以初始化列表至关重要。

1.4.2 特性

  1. 引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始化,否则会编译报错。
  2. C++11⽀持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显⽰在初始化列表初始化的成员使⽤的。
  3. 在初始化列表的成员-显式写;不在初始化列表的成员:a.声明的地方有缺省值用缺省值 b.没有缺省值:内置类型看编译器处理,自定义类型调用默认构造。如果没有默认构造会报错。
  4. 初始化列表中按照成员变量在类中声明顺序进⾏初始化,跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持⼀致。
    /*试分析下列程序*/
    #include<iostream>
    using namespace std;
    class test
    {
    public:
    	test(int a)
    		:_t1(a)
    		, _t2(_t1)
    	{}
    	void Print() {
    		cout << _t1 << " " << _t2 << endl;
    	}
    private:
    	int _t2 = 2;
    	int _t1 = 2;
    };
    int main()
    {
    	test t(1);
    	t.Print();
    }

    由于初始化列表是按照声明顺序初始化的,先初始化_t2,此时_t1没有初始化为随机值,所以结果如下

#include<iostream>
using namespace std;
class Test
{
public:
	Test(int a)
		:_t(a)
	{}
private:
	int _t;
};
class Date
{
public:
	Date(int& x, int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
		/*, _t(12)
		, _ref(x)
		, _n(1)*/
	{}
	void Print() const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	Test _t; // 没有默认构造
	int& _ref; // 引⽤
	const int _n; // const
};
int main()
{
	int i = 0;
	Date d1(i);
	d1.Print();
	return 0;
}

1.4.3 使用

由于每个构造函数都有初始化列表,建议以后都采用初始化列表的方式进行构造函数的初始化,使用中建议建议声明顺序和初始化列表顺序保持⼀致。

二、析构函数

2.1 概念

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

2.2 特性

  1. 析构函数名是在类名前加上字符~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  4. 析构函数不能重载。编译器自动调用
  5. 不写编译器会自动生成默认析构函数

2.3 使用

  1. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如
    Date类;
  2. 有资源申请时,必须要写,否则会造成资源泄漏,比如Stack类。
#include<iostream>
using namespace std;

class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_arr = (int*)malloc(sizeof(int) * capacity);
		if (_arr == NULL)
		{
			perror("malloc");
			exit(1);
		}
		_capacity = _capacity;
		_size = 0;
	}
	~Stack()
	{
		if (_arr)
		{
			free(_arr);
			_arr = NULL;
			_capacity = _size = 0;
		}
	}

private:
	int* _arr;
	int _capacity;
	int _size;
};

三、拷贝构造

3.1 概念

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

为什么必须是引用?

由C语言相关知识我们可以知道:传值调用实际上是传了当前变量的拷贝。所以如果拷贝构造函数的参数为传值调用,会引发无限的调用递归。所以要用传引用调用

3.2 特性

  1. 拷⻉构造函数是构造函数的⼀个重载。
  2. C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥⾃定义类型传值传参和传值返回都会调⽤拷⻉构造完成。
  3. 若未显式定义拷⻉构造,编译器会⽣成⾃动⽣成拷⻉构造函数。⾃动⽣成的拷⻉构造对内置类型成员变量会完成浅拷⻉(⼀个字节⼀个字节的拷⻉),对⾃定义类型成员变量会调⽤他的拷⻉构造。

浅拷贝:默认的浅拷贝仅复制指针的地址,两个对象共享同一块动态内存。如果一个对象释放了内存,另一个对象会出现悬空指针的问题。适用于对象不包含动态内存或对动态内存的共享是可接受的情况。
深拷贝:通过手动实现深拷贝构造函数和赋值运算符,确保每个对象都有自己独立的动态内存,避免悬空指针和共享内存的问题。适用于对象包含动态内存且需要独立内存空间的情况。

3.3 使用

有以下三种情况

  1. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器⾃动⽣成的拷⻉构造就可以完成需要的拷⻉,所以不需要我们显⽰实现拷⻉构造。
  2. 像Stack这样的类,虽然也都是内置类型,但是指向了资源,编译器⾃动⽣成的拷⻉构造完成的浅拷⻉不符合我们的需求,所以需要我们⾃⼰实现深拷⻉
  3. 像MyQueue这样的类型内部主要是⾃定义类型Stack成员,编译器自动生成的拷贝构造会调⽤Stack的拷⻉构造,也不需要我们显⽰实现MyQueue的拷⻉构造。
#include<iostream>
using namespace std;

class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_arr = (int*)malloc(sizeof(int) * capacity);
		if (_arr == NULL)
		{
			perror("malloc");
			exit(1);
		}
		_capacity = _capacity;
		_size = 0;
	}
	~Stack()
	{
		if (_arr)
		{
			free(_arr);
			_arr = NULL;
			_capacity = _size = 0;
		}
	}
	Stack(const Stack& s)
	{
		_arr = (int*)malloc(sizeof(int) * s._capacity);
		if (_arr == NULL)
		{
			perror("malloc");
			exit(1);
		}
		_capacity = s._capacity;
		_size = s._size;
		memcpy(_arr, s._arr, sizeof(int) * s._size);
	}

private:
	int* _arr;
	int _capacity;
	int _size;
};

注意事项:

传值返回会产⽣⼀个临时对象调⽤拷⻉构造。

传值引⽤返回,返回的是返回对象的别名(引⽤),没有产⽣拷⻉。

但是如果返回对象是⼀个当前函数局部域的局部对象,函数结束就销毁了,那么使⽤引⽤返回是有问题的,这时的引⽤相当于⼀个野引⽤,类似⼀个野指针⼀样。传引⽤返回可以减少拷⻉,但是⼀定要确保返回对象,在当前函数结束后还在,才能⽤引⽤返回。

四、赋值运算符重载

4.1 特性

  1. 赋值运算符重载是⼀个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成const 当前类类型引⽤,否则会传值传参会有拷⻉。
  2. 有返回值,且建议写成当前类类型引⽤,引⽤返回可以提⾼效率,有返回值⽬的是为了支持连续赋值场景。
  3. 没有显式实现时,编译器会⾃动⽣成⼀个默认赋值运算符重载,默认赋值运算符重载⾏为跟默认构造函数类似,对内置类型成员变量会完成浅拷⻉,对⾃定义类型成员变量会调⽤他的拷⻉构造。

4.2 使用

与拷贝构造函数使用相同,分为三类

  1. 像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器⾃动⽣成的赋值运算符重载就可以完成需要的拷⻉,所以不需要我们显⽰实现赋值运算符重载。
  2. 像Stack这样的类,虽然也都是内置类型,但是指向了资源,编译器⾃动⽣成的赋值运算符重载完成的浅拷⻉不符合我们的需求,所以需要我们⾃⼰实现深拷⻉。
  3. 像MyQueue这样的类型内部主要是⾃定义类型Stack成员,编译器⾃动⽣成的赋值运算符重载会调⽤Stack的赋值运算符重载,也不需要我们显⽰实现MyQueue的赋值运算符重载。
#include<iostream>
using namespace std;

class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_arr = (int*)malloc(sizeof(int) * capacity);
		if (_arr == NULL)
		{
			perror("malloc");
			exit(1);
		}
		_capacity = _capacity;
		_size = 0;
	}
	~Stack()
	{
		if (_arr)
		{
			free(_arr);
			_arr = NULL;
			_capacity = _size = 0;
		}
	}
	Stack(const Stack& s)
	{
		_arr = (int*)malloc(sizeof(int) * s._capacity);
		if (_arr == NULL)
		{
			perror("malloc");
			exit(1);
		}
		_capacity = s._capacity;
		_size = s._size;
		memcpy(_arr, s._arr, sizeof(int) * s._size);
	}
	Stack& operator=(const Stack& s)
	{
		//检查是否自己给自己赋值
		if (this != &s)
		{
			_arr = (int*)malloc(sizeof(int) * s._capacity);
			if (_arr == NULL)
			{
				perror("malloc");
				exit(1);
			}
			_capacity = s._capacity;
			_size = s._size;
			memcpy(_arr, s._arr, sizeof(int) * s._size);
		}
	}

private:
	int* _arr;
	int _capacity;
	int _size;
};
class Date
{
public:
	Date(int year = 1, 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;
	}
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		// d1 = d2 表达式的返回对象应该为d1 ,也就是*this
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

4.3 与拷贝构造的区别

  1. 赋值重载完成两个已经存在的对象直接的拷⻉赋值
  2. 拷⻉构适用于⼀个对象拷⻉初始化给另⼀个要创建的对象(还没创建)
int main()
{
	Date d1(2024, 7, 5);
	Date d2(d1);//拷贝构造
	Date d3(2024, 7, 6);
	d1 = d3;//赋值运算符重载
	Date d4 = d1;//拷贝构造
	return 0;
}

五、取地址操作符重载

5.1 cosnt

  • 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后⾯。
  • const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改。
    //以Date类为例
    //this指针由 Date* const this 变为 const Date* const this
    class Date
    {
    public:
        void Print() const
        {
        cout << _year << "-" << _month << "-" << _day << endl;
        }
    private:
        int _year;
        int _month;
        int _day;
    };

5.2 重载

取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器⾃动⽣成的就足够使用了,不需要去显⽰实现。

六、操作符重载补充

具体应用实现详见:

6.1 前置++与后置++

重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,⽆法很好的区分。所以C++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,方便区分。

6.2 流操作符重载

重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调⽤时就变成了 对象<<cout,不符合使⽤习惯和可读性。重载为全局函数把ostream/istream放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1958221.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

旷野之间32 - OpenAI 拉开了人工智能竞赛的序幕,而Meta 将会赢得胜利

他们通过故事做到了这一点&#xff08;Snapchat 是第一个&#xff09;他们用 Reels 实现了这个功能&#xff08;TikTok 是第一个实现这个功能的&#xff09;他们正在利用人工智能来实现这一点。 在人工智能竞赛开始时&#xff0c;Meta 的人工智能平台的表现并没有什么特别值得…

Java面试八股之@Qualifier的作用

Qualifier的作用 Qualifier 是 Spring 框架中的一个非常有用的注解&#xff0c;它主要用于解决在依赖注入过程中出现的歧义问题。当 Spring 容器中有多个相同类型的 Bean 时&#xff0c;Qualifier 可以帮助指明应该使用哪一个具体的 Bean 进行注入。 Qualifier 的作用&#x…

【error】AttributeError: module ‘cv2.dnn‘ has no attribute ‘DictValue‘(库冲突)

conda list conda remove opencv pip uninstall opencv-python conda list pip 同时卸载两个库 pip uninstall opencv-contrib-python opencv-python 没有and 直接写库名 module ‘cv2.dnn‘ has no attribute ‘DictValue‘解决办法_module cv2.dnn has no attribute d…

spark 3.0.0源码环境搭建

环境 Spark版本&#xff1a;3.0.0 java版本&#xff1a;1.8 scala版本&#xff1a;2.12.19 Maven版本&#xff1a;3.8.1 编译spark 将spark-3.0.0的源码导入到idea中 执行mvn clean package -Phive -Phive-thriftserver -Pyarn -DskipTests 执行sparksql示例类SparkSQLExam…

人工智能如何推动工业数字化转型?

随着科技的浪潮汹涌向前&#xff0c;人工智能&#xff08;AI&#xff09;正日益成为推动工业数字化发展的核心引擎。其强大的影响力不仅为工业生产注入了智能化、自动化的新活力&#xff0c;更在优化资源配置、提升生产效率以及实现个性化制造等关键领域展现出了无与伦比的潜力…

2020真题-架构师案例(五)

问题1&#xff08;13分&#xff09; 针对该系统的功能&#xff0c;孪工建议采用管道-过滤器&#xff08;pipe and filter&#xff09;的架构风格&#xff0c;而王工则建议采用仓库&#xff08;reposilory&#xff09;架构风格。满指出该系统更适合采用哪种架构风格&#xff0c…

数据库练习5

建立两个表:goods(商品表)、orders(订单表) 在商品表中导入商品记录并查看数据 建立触发器&#xff0c;订单表中增加订单数量后&#xff0c;商品表商品数量同步减少对应的商品订单出数量,并测试 下订单前查询数据库 下订单后查询数据库 建立触发器&#xff0c;实现功能:客户取消…

Matlab编程资源库(17)符号对象

一、建立符号对象 1 &#xff0e;建立符号变量和符号常量 MATLAB 提供了两个建立符号对象的函数&#xff1a; sym 和 syms &#xff0c;两个函数的用法不同。 (1) sym 函数 sym 函数用来建立单个符号量&#xff0c;一般调用格式为&#xff1a; 符号量名 sym( 符号字符串 )…

第一代iPad Mini完美降级8.4.1

文章目录 写在前面准备工作iOS 9.3.5版本越狱踩坑记录正确步骤越狱后设置 写在最后 写在前面 前几天打扫房间&#xff0c;不小心翻出来了10年前的第一代iPad Mini&#xff0c;版本升级到9.3.5之后&#xff0c;基本上算是报废了&#xff0c;运行啥都卡成狗&#xff1b;但是买不…

R语言 爬取数据+简单清洗

小小练习。见代码注释 # 加载必要的包 library(rvest) library(dplyr) library(tidyr)# 指定网页URL url <- "https://research.un.org/en/unmembers/scmembers"# 读取网页内容 webpage <- read_html(url)# 提取所有表格节点 table_nodes <- html_nodes(web…

聊聊基于Alink库的特征工程方法

独热编码 OneHotEncoder 是用于将类别型特征转换为独热编码的类。独热编码是一种常用的特征编码方式&#xff0c;特别适用于处理类别型特征&#xff0c;将其转换为数值型特征。 对于每个类别型特征&#xff0c;OneHotEncoder 将其编码成一个长度为类别数量的向量。 每个类别对…

使用 Python 进行马尔可夫链职业路径建模

欢迎来到雲闪世界。从职业角度来说&#xff0c;我是个非常奇怪的人&#xff1a;我在一家初创公司担任软件/机器学习工程师&#xff0c;拥有物理学硕士学位&#xff0c;即将为航空航天和机械工程博士学位论文答辩。在我不断变化的职业生涯中&#xff0c;有两件事始终不变&#x…

Java9-21的开发相关新特性总结

目录 下载地址 Java 21(LTS) 概述 变动说明 1、JEP 441: Switch 的模式匹配&#xff08;正式特性&#xff09; 功能进化 Switch 模式匹配 类型标签 null标签 守卫标签 使用enum常量作值 语法总结 2、JEP 440&#xff1a;Record模式&#xff08;正式特性&#xff09…

GPU的shader分支跳转性能总结

引言&#xff1a; 如下的&#xff08;一&#xff09;与&#xff08;二&#xff09;分别属于uniform branch与宏定义&#xff0c;&#xff08;一&#xff09;至始至终是一个固定的值&#xff0c;分支只执行一条而不是既有执行condition ture 也有执行condition false 的情况&am…

基于CentOS Stream 9平台安装MySQL Community Server 9.0.1 Innovation

1. 安装之前 1.1 查看系统版本 cat /etc/redhat-releaseCentOS Stream release 9 1.2 查看cpu架构 lscpu架构&#xff1a; x86_64 CPU 运行模式&#xff1a; 32-bit, 64-bit 2. 官网下载 https://dev.mysql.com/downloads/mysql/ 要多看看 官方9.0文档&#xff1a;https://d…

前端面试基础题(微信公众号:前端面试成长之路)

BFC、IFC、GFC、FFC CSS2.1中只有BFC和IFC, CSS3中才有GFC和FFC。 到底什么是BFC、IFC、GFC和FFC Whats FC&#xff1f; 一定不是KFC&#xff0c;FC的全称是&#xff1a;Formatting Contexts&#xff0c;是W3C CSS2.1规范中的一个概念。它是页面中的一块渲染区域&#xff0c;并…

【C++进阶学习】第九弹——哈希的原理与实现——开放寻址法的讲解

前言&#xff1a; 在前面&#xff0c;我们已经学习了很多存储机构&#xff0c;包括线性存储、树性存储等&#xff0c;并学习了多种拓展结构&#xff0c;效率也越来越高&#xff0c;但是是否有一种存储结构可以在大部分问题中都一次找到目标值呢&#xff1f;哈希可能能实现 目录…

npm国内淘宝镜像registry镜像过期

我们在使用npm的时候会遇到淘宝镜像安装过期的问题 首先了解 npm 淘宝镜像是一个 npm 注册表的镜像&#xff0c;用于加速 npm 包的下载。 一、如何设置&#xff1f; 如何设置淘宝镜像&#xff1f;淘宝镜像已经从 registry.npm.taobao.org 切换到了 registry.npmmirror.com n…

【书生大模型实战营(暑假场)】入门任务一 Linux+InternStudio 关卡

入门任务一 LinuxInternStudio 关卡 参考&#xff1a; 教程任务 1 闯关任务 1.1 基于 VScode 的 SSH 链接 感谢官方教程的清晰指引&#xff0c;基于VS code 实现 SSH 的链接并不困难&#xff0c;完成公钥配之后&#xff0c;可以实现快速一键链接&#xff0c;链接后效果如下…

搭建自动化 Web 页面性能检测系统 —— 部署篇

作为一个前端想去做全栈的项目时&#xff0c;可能第一个思路是 node vue/react。一开始可能会新建多个工程目录去实现&#xff0c;假设分别为 web 和 server&#xff0c;也许还有管理后台的代码 admin&#xff0c;那么就有了三个工程的代码。此时为了方便管理就需要在远程仓库…