C++-类和对象(上)

2023-11-01

一,构造函数

1,概念

class Stack
{
public:
	void Init(int n = 4)
	{
		_a = (int*)malloc(sizeof(int) * n);
		if (!_a)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capcity = n;
	}
	void Destroy()
	{
		if (_a)
		{
			free(_a);
			_a = nullptr;
			_top = _capcity = 0;
		}
	}
private:
	int* _a;
	int _top;
	int _capcity;
};

int main()
{
	Stack st;
	st.Init();
	return 0;
}

由于C++的类内允许定义函数,但是像上述那样需要我们去手动的初始化对象很容易玩掉。于是C++中提出了构造函数这一概念。

构造函数 :构造函数是一个特殊的成员函数,名字与类名相同,在类的实例化时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象的整个生命周期内只调用一次。
注意:构造函数不是用来创建对象的,而是用来初始化对象的,构造函数是没有返回值的,且支持函数重载。

所以,一般在C++中是这样写的:

class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (int*)malloc(sizeof(int) * n);
		if (!_a)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capcity = n;
	}
private:
	int* _a;
	int _top;
	int _capcity;
};
int main()
{
	Stack st1;
	Stack st2(8);
	return 0;
}

在这里插入图片描述

切忌这样使用无参的构造函数:

Stack st3();

这会与函数的声明冲突,函数名为st3,参数为空,返回值为Stack的函数声明。

2,特性

  • 函数名与类名相同
  • 无返回值
  • 类的实例化时编译器自动调用其构造函数
  • 支持函数重载
  • 如果没有显示的定义构造函数,编译器会自动生成一个默认构造函数,一旦用户显示定义了构造函数,那么编译器就不会自动生成
  • 编译器自动生成的默认构造函数,对内置类型不做处理,对自定义类型会去调用其默认构造函数
class Queue
{
public:

private:
	Stack st1;
	Stack st2;
	int _size;
};
int main()
{
	Queue q;
	return 0;
}

在这里插入图片描述

即使Queue这个类没有显示定义构造函数,所以编译器自动生成了一个默认构造函数,对自定义类型会去调用其默认构造,对内置类型不做处理(可以看到编译器也对_size进行了初始化,这只是针对本编译器会这样做,不具有普遍性)。

  • 针对编译器自动生成的默认构造函数对内置类型不做处理,在C++ 11中提出允许给非静态成员变量提供一个缺省值,即在类的实例化时未对某个成员变量初始化,那么其的值就为所给的缺省值。
class Queue
{
public:

private:
	Stack st1;
	Stack st2;
	int _size = 1;
};
int main()
{
	Queue q;
	return 0;
}

在这里插入图片描述

  • 无参的构造函数与全缺省的构造函数与编译器自动生成的都是默认构造函数,且默认构造函数只允许有一个。

二,析构函数

1,概念

void Destroy()
{
	if (_a)
	{
		free(_a);
		_a = nullptr;
		_top = _capcity = 0;
	}
}

与构造函数的功能相反,当我们定义的对象涉及到动态内存开辟时,之前我们会手动的去调用Destroy函数去进行资源管理,析构函数在对象销毁的时候自动调用,完成对象中资源的清理工作,且函数名为 ~类名 ,没有参数,不支持函数重载。

class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (int*)malloc(sizeof(int) * n);
		if (!_a)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capcity = n;
	}
	~Stack()
	{
		if (_a)
		{
			free(_a);
			_a = nullptr;
			_top = 0;
			_capcity = 0;
		}
	}
private:
	int* _a;
	int _top;
	int _capcity;
};

2,特性

  • 函数名 ~类名
  • 无参数
  • 不支持函数重载
  • 对象销毁时自动调用
  • 当用户没有显示的去写析构函数时,编译器会自动生成一个析构函数。
  • 编译器自动生成的析构函数,对内置类型不做处理,对于自定义类型会去调用其析构函数。
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (int*)malloc(sizeof(int) * n);
		if (!_a)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capcity = n;
	}
	
private:
	int* _a;
	int _top;
	int _capcity;
};
void test1()
{
	Stack st1(6);
}
int main()
{
	test1();
	return 0;
}

Stack 类中的成员变量都是内置类型,编译器自动生成的析构函数不对其做处理,导致_a指向的空间没有被释放,造成内存泄露。

  • 如果类中没有涉及申请资源时,析构函数可以不用写,直接使用编译器自动生成的析构函数,像Stack类这样的涉及申请资源时,一定要写析构函数

三,拷贝构造

1,概念

拷贝构造函数是构造函数的一种重载形式,只有一个参数,是本类对象的引用(一般用const 修饰),用已有对象去初始化创建新的对象时,会自动调用拷贝构造。

2,特性

  • 拷贝构造函数是构造函数的一种重载形式
  • 拷贝构造函数的参数只有一个且必须是同类对象的引用,使用传值的方式编译器会报错,因为会发生无穷递归。
    如果调用拷贝构造使用传值的方式:由于传值传参会发生拷贝构造,也就是说我们还没调用到拷贝构造之前,要现发生一次拷贝构造,依次反复,无穷的递归下去。
  • 如果没有显示的定义拷贝构造函数,编译器会自动生成一个拷贝构造。其对内置类型进行简单的值拷贝,对自定义类型会调用其拷贝构造函数。
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 2, 10);
	Date d2(d1);
	return 0;
}

在这里插入图片描述

  • 当类中涉及到资源的申请时,一定也要写显示的拷贝构造函数,否则自动生成的是浅拷贝。
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (int*)malloc(sizeof(int) * n);
		if (!_a)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capcity = n;
	}
	~Stack()
	{
		if (_a)
		{
			free(_a);
			_a = nullptr;
			_top = 0;
			_capcity = 0;
		}
	}
private:
	int* _a;
	int _top;
	int _capcity;
};
int main()
{
	Stack st1;
	Stack st2(st1);
	return 0;
}

在这里插入图片描述

可以看到如果使用把编译器提供的拷贝构造,会发生浅拷贝,会导致一系列错误的出现。

Stack(const Stack& st)
	{
		if (this != &st)
		{
			_a = (int*)malloc(sizeof(int) * st._capcity);
			if (!_a)
			{
				perror("malloc fail");
				exit(-1);
			}
			memcpy(_a, st._a, sizeof(int) * st._capcity);
			_top = st._top;
			_capcity = st._capcity;
		}
	}

四,运算符重载

1,概念

int main()
{
	vector<int> a(4, 0);
	for (int i = 0; i < 4; i++)
	{
		a[i] = i;
	}
	for (int i = 0; i < 4; i++)
	{
		cout << a[i] << " ";
	}
	return 0;
}

在这里插入图片描述

vector是STL库中提供的顺序表这一数据结构,但是你发现他居然能像数组一样使用[ ]来访问其成员,其实这是重载了[ ]这个运算符,本质上是函数调用。

运算符重载: C++中为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有返回值,函数名,参数列表,且与普通函数类似。其函数名为operator(关键字)+ 要重载的运算符

例如,我们自己实现一个数组类来运用一下这个运算符重载:

class Array
{
public:
	int& operator[](int i)
	{
		return a[i];
	}
private:
	int a[10];
	int size = 0;
};

int main()
{
	Array arr1;
	for (int i = 0; i < 5; i++)
	{
		arr1[i] = i;
	}
	for (int i = 0; i < 5; i++)
	{
		cout << arr1[i] << " ";
	}
	return 0;
}

在这里插入图片描述
注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载运算符必须有一个参数是自定义类型的
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .* :: sizeof ?: . 注意以上5个运算符不能重载。

2,赋值运算符重载

1,运算符重载格式

  • 参数类型:const T&,传递引用可以提高传参效率,同时也防止了由于赋值的顺序产生-错误。
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义

2,赋值运算符只能重载成类的成员函数不能重载成全局函数
==原因:==赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

3,用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator[](const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};

五,&重载与const& 重载

这两个默认成员函数一般不用自己实现,编译器会默认生成。

class Date
{
public :
Date* operator&()
{
	return this ;

}
const Date* operator&()const
{
	return this ;
}
private :
	int _year ; // 年
	int _month ; // 月
	int _day ; // 日
};

六,总结

1,类的6个默认成员函数

  • 构造函数
  • 析构函数
  • 拷贝构造函数
  • 赋值运算符重载
  • &运算符重载
  • const& 运算符重载

2,默认成员函数共同的性质就是,用户不主动提供时,编译器会自动提供默认成员函数。
3,编译器自动提供的构造函数与析构函数,对内置类型不做处理,对与自定义类型会调用其构造函数和析构函数。
4,编译器自动提供的赋值运算符重载 拷贝构造,对内置类型会进行值拷贝,对自定义类型调用其赋值运算符重载和拷贝构造。
5,C++ 11 提供了可以给内置类型提供缺省值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++-类和对象(上) 的相关文章

  • Spring Data JPA:查询如何返回非实体对象或对象列表?

    我在我的项目中使用 Spring Data JPA 我正在演奏数百万张唱片 我有一个要求 我必须获取各种表的数据并构建一个对象 然后将其绘制在 UI 上 现在如何实现我的 Spring 数据存储库 我读到它可以通过命名本机查询来实现 如果指
  • if constexpr 中的 not-constexpr 变量 – clang 与 GCC

    struct A constexpr operator bool const return true int main auto f auto v if constexpr v A a f a clang 6 接受该代码 GCC 8 拒绝它
  • Linux 上的 RTLD_LOCAL 和dynamic_cast

    我们有一个由应用程序中的一些共享库构成的插件 我们需要在应用程序运行时更新它 出于性能原因 我们在卸载旧插件之前加载并开始使用新插件 并且只有当所有线程都使用旧插件完成后 我们才卸载它 由于新插件和旧插件的库具有相同的符号 我们dlopen
  • C# 获取数据表中所有重复行的计数

    我通过运行存储过程来填充数据集 并且从数据集中填充数据表 DataSet RawDataSet DataAccessHelper RunProcedure storedprocedureName this will just return
  • 避免 Java 中的重复导入:继承导入?

    有没有办法 继承 导入 Example 常见枚举 public enum Constant ONE TWO THREE 使用此枚举的基类 public class Base protected void register Constant
  • Lombok @Builder 不创建不可变对象?

    在很多网站上 我看到 lombok Builder 可以用来创建不可变的对象 https www baeldung com lombok builder singular https www baeldung com lombok buil
  • Unity c# 四元数:将 y 轴与 z 轴交换

    我需要旋转一个对象以相对于现实世界进行精确旋转 因此调用Input gyro attitude返回表示设备位置的四元数 另一方面 这迫使我根据这个四元数作为默认旋转来计算每个旋转 将某些对象设置为朝上的简单方法如下 Vector3 up I
  • 我可以限制分布式应用程序发出的请求吗?

    我的应用程序发出 Web 服务请求 提供商处理的请求有最大速率 因此我需要限制它们 当应用程序在单个服务器上运行时 我曾经在应用程序级别执行此操作 一个对象跟踪到目前为止已发出的请求数量 并在当前请求超出允许的最大负载时等待 现在 我们正在
  • 当Model和ViewModel一模一样的时候怎么办?

    我想知道什么是最佳实践 我被告知要始终创建 ViewModel 并且永远不要使用核心模型类将数据传递到视图 这就说得通了 让我把事情分开 但什么是Model 和ViewModel一模一样 我应该重新创建另一个类还是只是使用它 我觉得我应该重
  • 如何在C#中控制datagridview光标移动

    我希望 datagridview 光标向右移动到下一列 而不是在向单元格输入数据后移动到下一行 我试图通过 dataGridView1 KeyDown 事件捕获键来控制光标 但这并不能阻止光标在将数据输入到单元格后移动到下一行 提前感谢你的
  • 如何处理 StaleElementReferenceException

    我正在为鼠标悬停工作 我想通过使用 for 循环单击每个链接来测试所有链接的工作条件 在我的程序中 迭代进行一次 而对于下一次迭代 它不起作用并显示 StaleElementReferenceException 如果需要 请修改代码 pub
  • 在 C# 的 WebAPI 中的 ApiController 上使用“传输编码:分块”提供数据

    我需要服务分块传输使用编码数据API控制器 因为我无权访问HttpContext or the Http请求 我有点不知道在哪里写入响应以及在哪里刷新它 设置如下 public class MyController ApiControlle
  • 任何人都可以清楚地告诉如何在不使用像 这样的预定义函数的情况下找到带有小数值或小数值的指数吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 例如 2 0 5 1 414 所以想要 我是 c 的新手 所以请解释简单的逻辑 如果不是复杂的逻辑也足够了 在数学中 从整数取幂到实数
  • 使用 JFreeChart 为两个系列设置不同的 y 轴

    我正在使用 JFreeChart 使用折线图绘制两个数据系列 XYSeries 复杂的因素是 其中一个数据系列的 y 值通常远高于第二个数据系列的 y 值 假设第一个系列的 y 值约为数百万数量级 而第二个数据系列的 y 值约为数百万数量级
  • 将对象从手机共享到 Android Wear

    我创建了一个应用程序 在此应用程序中 您拥有包含 2 个字符串 姓名和年龄 和一个位图 头像 的对象 所有内容都保存到 sqlite 数据库中 现在我希望可以在我的智能手表上访问这些对象 所以我想实现的是你可以去启动 启动应用程序并向左和向
  • 从后面的代码添加外部 css 文件

    我有一个 CSS 文件 例如 SomeStyle css 我是否可以将此样式表文档从其代码隐藏应用到 aspx 页面 您可以将文字控件添加到标头控件中 Page Header Controls Add new System Web UI L
  • 如何使用通配符模拟泛型方法的行为

    我正在使用 EasyMock 3 2 我想基于 Spring Security 为我的部分安全系统编写一个测试 我想嘲笑Authentication http docs spring io autorepo docs spring secu
  • 基于 Spring Boot 的测试中的上下文层次结构

    我的 Spring Boot 应用程序是这样启动的 new SpringApplicationBuilder sources ParentCtxConfig class child ChildFirstCtxConfig class sib
  • 如果找不到指定的图像文件,显示默认图像的最佳方式?

    我有一个普通的电子商务应用程序 我将 ITEM IMAGE NAME 存储在数据库中 有时经理会拼错图像名称 为了避免 丢失图像 IE 中的红色 X 每次显示产品列表时 我都会检查服务器中是否有与该产品相关的图像 如果该文件不存在 我会将其
  • ContentDialog Windows 10 Mobile XAML - 全屏 - 填充

    我在项目中放置了一个 ContentDialog 用于 Windows 10 上的登录弹出窗口 当我在移动设备上运行此项目时 ContentDialog 未全屏显示 并且该元素周围有最小的填充 在键盘上可见 例如在焦点元素文本框上 键盘和内

随机推荐

  • Linux 非阻塞connect

    套接字执行I O操作有阻塞和非阻塞两种模式 在阻塞模式下 在I O操作完成前 执行操作的函数一直等候而不会立即返回 该函数所在的线程会阻塞在这里 相反 在非阻塞模式下 套接字函数会立即返回 1 而不管I O是否完成 该函数所在的线程会继续运
  • https流程详解(含ca证书校验)

    首先来说 一下 为什么需要https 加密 由于http 传输是明文传输 信息在传输的过程中容易被篡改 不安全 所以 就有了 加密传输 对称加密 aes等 对称加密很容易理解 就是 只有一个秘钥 可以使用这个秘钥加密和解密 但是这样只要被别
  • 关系型数据库RDBMS -MySQL基础入门(五)数据备份与恢复

    一 物理备份 innodbackupex 二 逻辑备份 mysqldump Binlog 二进制 日志文件 备份恢复 主从同步 除查询sql所有命令都记录 索引文件 bin index bin 000001 Purge master log
  • 蓝牙之九-AT命令

    AT命令用于HF协议 该命令使参考3GPP 27 007协议 以下是HFP规范 每个命令行只有一个命令 AG侧默认不回显命令 AG使用冗长的格式返回结果 以下字符将被用于AT命令和返回结果格式中
  • Spring MVC详解

    第一节 Spring MVC 简介 1 Spring MVC SpringMVC是一个Java 开源框架 是Spring Framework生态中的一个独立模块 它基于 Spring 实现了Web MVC 数据 业务与展现 设计模式的请求驱
  • Session的生命周期

    以前在学习的时候没怎么注意 今天又回过头来仔细研究研究了一下Session的生命周期 Session存储在服务器端 一般为了防止在服务器的内存中 为了高速存取 Sessinon在用户访问第一次访问服务器时创建 需要注意只有访问JSP Ser
  • 什么是DDL和DML语句?

    1 什么是DDL和DML语句 SQL语言分成三大类 1 DDL语言 2 DML语言 3 DCL语言 可恶 竟然在技术内幕只提及了一部分 内幕661 要知道DDL是什么意思 必须先了解它的英文全称 data definition langua
  • 【华为OD机试】TLV解析Ⅰ【2023 B卷

    华为OD机试 真题 点这里 华为OD机试 真题考点分类 点这里 题目描述 TLV 编码是按 Tag Length Value 格式进行编码的 一段码流中的信元用Tag标识 Tag在码流中 唯一不重复 Length表示信元Value的长度 V
  • Linux 定时任务crontab配置

    https www cnblogs com zoulongbin p 6187238 html
  • [718]canal的配置详解

    文章目录 canal的配置加载方式 properties配置文件 canal properties介绍 instance properties介绍 canal的配置加载方式 介绍配置之前 先了解下canal的配置加载方式 canal配置方式
  • 【斯坦福CS224W笔记之一】 图的基本表示

    是来自b站up主同济子豪兄的中文精讲 自己做来总结给自己学习滴 如果感兴趣的话可以去b站搜索或者去子豪兄的github图神经进行学习 https github com TommyZihao zihao course blob main CS
  • Java线程安全与等待通知

    目录 1 线程不安全原因 1 1 引入 线程不安全的例子 抢占式执行 1 2 线程不安全的原因 5点 其他 2 抢占式执行引起的线程不安全 synchronized 3 内存可见性引起的线程不安全 volatile 3 1 例子 编译器优化
  • Win10操作系统隐藏6个实用小功能

    目录 功能一 分屏 功能二 录屏 功能三 截图 功能四 便签功能 功能五 视频剪辑 功能六 计算器 功能一 分屏 Win10操作系统其实是自带分屏功能的 这个功能对我来说真的太喜欢了 尤其是核对文档的时候 真的是太方便了 操作方法 在文档页
  • 内网离线安装Docker

    文章目录 描述 下载docker离线包地址 1 安装docker 1 下载 Docker 二进制文件 离线安装包 2 上传离线包 3 解压安装包 4 将docker 相关命令拷贝到 usr bin 5 docker注册成系统服务 6 添加执
  • QT--信号槽与带参Lambda表达式

    一 常规的不带参数的Lambda表达式响应槽函数 connect ui btn QushButton clicked to do something 二 带参的Lambda表达式响应槽函数 connect ui spbox static c
  • ArcSDE超出最大连接数问题解决

    ARCSDE数据库默认连接的实例数为48 可用 sdemon o info I users 命令查看当前的用户链接数 因为连接的用户很多时 会导致实例数不够用 这个时候需要对连接的实例数进行修改 修改方法如下 1 plsql登录sde用户
  • 你还没有Aspose 2021中文开发指南吗?中文指南打包带走

    说到文档处理 就得来说说Aspose系列 Aspose是全球领先的图表控件开发商 旗下产品覆盖Word Excel PDF 条码 ZIP CAD HTML 电子邮件 PSD等各个文档管理领域 为全球 NET Java C 等各大平台程序员提
  • ARM平台移植libcurl curl-7.49.0

    libcurl是免费的轻量级的客户端网络库 支持DICT FILE FTP FTPS Gopher HTTP HTTPS IMAP IMAPS LDAP LDAPS POP3 POP3S RTMP RTSP SCP SFTP SMTP SM
  • SpringMVC重要接口(三)HttpMessageConverter

    大部分人的第一反应是通过SpringMVC拦截器 Interceptor 中的postHandler方法处理 实际这是行不通的 因为当程序运行到该方法 是在返回数据之后 渲染页面之前 所以这时候HttpServletResponse中的输出
  • C++-类和对象(上)

    类和对象 上 一 构造函数 1 概念 2 特性 二 析构函数 1 概念 2 特性 三 拷贝构造 1 概念 2 特性 四 运算符重载 1 概念 2 赋值运算符重载 五 重载与const 重载 六 总结 一 构造函数 1 概念 class St