详解C++之类和对象(2)

目录
  • 一.构造函数
    • 1.构造函数的定义:
    • 2.构造函数的特征:
    • 3.构造函数的实现:
      • 3.1.系统默认的构造函数
      • 3.2无参构造
      • 3.3 带参构造
  • 二 析构函数
    • 1.析构函数的定义
    • 2.析构函数的特征
  • 三 拷贝函数
    • 1.拷贝函数定义
    • 2.拷贝函数的特性
    • 3.拷贝函数的实现
  • 总结

一.构造函数

1.构造函数的定义:

构造函数 是一个 特殊的成员函数,名字与类名相同 , 创建类类型对象时由编译器自动调用 ,保证每个数据成员都有 一个合适的初始值,并且 在对象的生命周期内只调用一次 。 其实构造函数的作用就是完成成员变量的初始化 ,但不同于c语言的初始化构造函数可以实在创造对象的同时就完成成员变量的初始化。

2.构造函数的特征:

1. 函数名与类名相同。

2. 无返回值。

3. 对象实例化时编译器 自动调用 对应的构造函数。

4. 构造函数可以重载。

3.构造函数的实现:

构造函数的实现主要有三种,

1.当用户没有实现构造函数的话系统会默认创造一个,此时系统会将内置类型的成员变量赋予随机值,而对于自定义类型的成员变量则会调用他们的构造函数。(注:内置类型一般指的是:int char double float等这些定义好的类型,自定义类型指的是:struct这种类型以及class类这种)。

2.当然用户也可以自己实现构造函数,一种为无参构造

3.类一种为带参构造,但是在带参构造中我们使用全缺省构造。我们用代码展示一下:

3.1.系统默认的构造函数

我们可以看到当我们没有在Data类进行函数构造的时系统将会自己默认创建构造函数,对内置类型变量赋予随机值,自定义类型调用自己的构造函数(若自定义类型也没有定义构造函数那么此例子中的_a1和_a2也会被赋予随机值)

3.2无参构造

3.3 带参构造

这里出一个问题对于代码风格造成的问题:成员变量year最后的结果是多少呢?

class A{public:A(int year){year = year;}private:int year;};int main(){A a(20);}

答案是:随机值。那么为什么是随机值呢?这里主要是变量之间它采用了就近原则,所以等式左边的year会直接寻找离他最近的变量所以会将等式右边的year直接赋值给它自己,所以year最后的值就是随机值。

我们继续来说带参的构造函数,我们一般推荐使用的是全缺省的构造函数(注:

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,三者都可以认为是默认成员函数。

二 析构函数

构造函数时完成对象的初始化,那么一个对象又是怎么样被销毁的呢?

1.析构函数的定义

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

2.析构函数的特征

1. 析构函数名是在类名前加上字符 ~ 。

2. 无参数无返回值。

3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数 。

4. 对象生命周期结束时, C++ 编译系统系统自动调用析构函数。

这里我们用栈的例子来说明析构函数的实现以及作用。

class Stack
	{
	public:
		Stack(int capacity = 4)
		{
			_a = (int*)malloc(sizeof(int)*capacity);
			if (_a == nullptr)
			{
				cout << "malloc fail" << endl;
				exit(-1);
			}
			_top = 0;
			_capacity = capacity;
		}
	//析构函数的实现
		~Stack()
		{
			// 像Stack这样的类,对象中的资源需要清理工作,就用析构函数
			free(_a);
			_a = nullptr;
			_top = _capacity = 0;
		}
 	private:
		int* _a;
		int _top;
		int _capacity;
	};

这里是完成构造函数,有自己定义的析构函数的效果。同构造函数一样对于内置成员变量析构函数会置为随机值,而自定义类型则会去调用他们的析构函数。

三 拷贝函数

如果某些时候我们需要去复制一个对象,这样的话我们该怎么样去解决呢?

这里我们就需要引入拷贝函数。那么什么叫做拷贝函数呢?我们应该去怎么实现呢?有什么注意事项呢?这里我们一一来说道。

1.拷贝函数定义

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

2.拷贝函数的特性

1. 拷贝构造函数 是构造函数的一个重载形式 。

2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用 。

3. 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷 贝,这种拷贝我们叫做浅拷贝,或者值拷贝。

3.拷贝函数的实现

拷贝函数的实现分为两种一种是系统默认,一种是自己定义。我们分别来看其效果

class A
{
public:
	A()
	{
         _a1 = 1;
		 _a2 = 2;
	}
	~A()
	{
		cout << "A()" << endl;
	}
private:
	int _a1;
	int _a2;
};
 class Data
{
public:
	/*Data()
	{
		_year = 2021;
		_month = 12;
		_day = 12;
	}*/
	//Data(int year, int month, int day)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}
	Data(int year = 2022,
		int month = 12,
		int day = 12)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
	A a;
};
 int main()
{
	Data s;
	//拷贝函数的调用
	Data s2(s);
	return 0;
}

调用系统默认生成拷贝函数(注:这里拷贝函数的拷贝对自定义类型和内置类型的成员变量处理都是一致的完成字节序的值拷贝)

图1 调用系统默认生成的拷贝函数

图2 调用用户自己定义的拷贝函数

在这里我们顺便说一下在自定义拷贝函数的时候一定要使用引用不然会出现无限递归例如 Data(Data s){}正确的使用是Data (const Data & s){}其中const是为了保护原数据不被轻易改动。

class A
{
public:
	A()
	{
         _a1 = 1;
		 _a2 = 2;
	}
	~A()
	{
		cout << "A()" << endl;
	}
private:
	int _a1;
	int _a2;
};
 class Data
{
public:
	/*Data()
	{
		_year = 2021;
		_month = 12;
		_day = 12;
	}*/
	//Data(int year, int month, int day)
	//{
	//	_year = year;
	//	_month = month;
	//	_day = day;
	//}
	Data( const Data &s)
	{
		_year = s._year;
		_month = s._month;
		_day = s._day;
 	}
	Data(int year = 2023,
		int month = 12,
		int day = 12)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
	A a;
};
 int main()
{
	Data s;
	//拷贝函数的调用
	Data s2(s);
	return 0;
}

我们可以发现s2均完整的赋值了s的内容,但是这里真的就没有问题了吗?如果我们使用系统默认生成的拷贝函数成员变量中含有指针那么会出现什么样的问题呢?

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 s;
String s1(s);
}

我们可以看到虽然虽然s1拷贝了s的内容但是最后系统还是抛出了错误那么这个错误来自那里呢?

我们看这幅图

这里就是我们之前说的系统默认生成的拷贝函数是浅拷贝,那么怎么去完成深拷贝我们后边在继续讲解。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • C++学习笔记之类与对象详解

    目录 前言: 1.访问限定符: [问题]C++中 struct和class的区别是什么? 2.封装 [问题]在类和对象的阶段,我们只研究类的封装特性,那什么是封装呢? 3.类的定义与声明 [问题]函数调用的问题 4.类的作用域 5.类的实例化 6.类的分类 总结 前言: 1.C 语言是面向过程的,关注的是过程,分析出求解的步骤,通过函数逐步调用解决问题. 2.C++是基于面向对象的,关注的是对象,蒋一件事情拆分成不同的对象,靠对象之间的交互完成. 举个例子:外卖系统 面向过程是下单.接单.送餐的

  • 详解C++之类和对象(1)

    目录 C语言和C++的一大区别 struct和class的区别 总结 c++类的由来(或者说为什么要增加类):我们知道c语言是面向过程的语言,c++是即面向过程又是面向过程的语言.那么这两个有着什么样的区别和联系呢? C语言和C++的一大区别 接下来我们利用c语言的struct来说明: C 语言中,结构体中只能定义变量. 在 C++ 中,结构体内不仅可以定义变量,也可以定义函数. 例如:如果我们用C语言实现一下的功能,那么我们会发现,在struct内我们只能去定义一些变量 的类型,而我们需要的函

  • C++类和对象之多态详解

    目录 多态基本概念和原理剖析 多态案例1 计算器类 纯虚函数和抽象类 多态案例2 制作饮品 虚析构和纯虚析构 多态案例3 电脑组装 多态基本概念和原理剖析 多态:多态是C++面向对象的三大特性之一.多态分为静态多态和动态多态. 静态多态:函数重载和运算符重载属于静态多态,复用函数名. 动态多态:派生类和虚函数实现运行时多态. 区别: 静态多态的函数地址早绑定,编译阶段确定函数地址. 动态多态的函数地址晚绑定,运行阶段确定函数地址. #include <iostream> using names

  • C++类和对象实战之Date类的实现方法

    目录 零.前言 一.Date类相关接口 二.具体接口函数实现 1.获取月份天数 2.Date打印 3.Date构造函数 4.Date析构函数 5.Date拷贝构造函数 6.Date赋值重载函数 7.Date+=天数 8.Date+天数 9.Date-=天数 10.Date-天数 11.++Date 12.Date++ 13.–Date 14.Date– 15.日期比较 16.Date相减 17.日期输入\日期输出 总结 零.前言 在学了C++类和对象基本知识以及六个默认成员函数后,我们可以上手实

  • C++类和对象之封装详解

    目录 封装的意义以及示例 访问权限 公共权限 public 保护权限 protected 私有权限 private struct 和 class的区别 成员属性私有化 案例1:设计立方体类 案例2:点和圆的关系 总结 封装的意义以及示例 封装是C++面向对象三大特征之一 封装的意义: 将属性和行为作为一个整体,表现生活中的事物将属性和行为加以权限控制 语法:class 类名{  访问权限 : 属性  /  行为 }: 类的对象的公共数据成员可以使用直接成员访问运算符 . 来访问. 示例1: 设计

  • c++类和对象基本概念

    目录 什么是类? 什么是对象? 类的定义 创建对象 成员访问(初始化) 总结 什么是类? 一系列事物的抽象,对于c++而言,万事万物都可以是类. 类包括:属性+行为 属性:事物特征->数据类型描述: 行为事物的操作->函数描述: 什么是对象? 类的具体化,类的实例化,抽象->具象: 类的特点:封装.继承.派生.多态. 类的定义 创建方法:class class 类名{ //权限限定词 public: protected://保护属性 private://当不做继承时,数据成员写成私有属性

  • 详解 Python 与文件对象共事的实例

    详解 Python 与文件对象共事的实例 Python 有一个内置函数,open,用来打开在磁盘上的文件.open 返回一个文件对象,它拥有一些方法和属性,可以得到被打开文件的信息,以及对被打开文件进行操作. >>> f = open("/music/_singles/kairo.mp3", "rb") (1) >>> f (2) <open file '/music/_singles/kairo.mp3', mode 'r

  • 详解 C# 中XML对象的序列化和反序列化

    这一篇主要是用来介绍关于C#中的XML序列化的问题,这个相信大家一定会经常使用它,特别是在WPF中,有时候我们需要将我们后台的数据保存在数据库中,从而在软件下一次启动的时候能够自动去加载这些数据,由于我们的这些Model中字段众多,如果单独进行保存那是不太现实的,这个时候将这些字段序列化成xml字符串并保存在数据库中就是一个不错的选择,当我们需要这些数据的时候我们也可以反过来将其序列化为一些字段,最终达到我们的效果,首先我们来看看是如何实现的? public class XMLUtil {   

  • 详解WPF中的对象资源

    在WPF中,所有继承自FrameworkElement的元素都包含一个Resources属性,这个属性就是我们这篇要讲的资源. 这一篇讲解的资源是不是上一篇的程序集资源(那个是在编译过程中打包到程序集中),这个是资源是我们想在公共的地方写一个对象让其他元素重复使用. 先贴个例子: <Window x:Class="NETResource.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/pre

  • 详解JS中的对象字面量

    前言 在 ES6 之前,js中的对象字面量(也称为对象初始化器)是非常基础的.可以定义两种类型的属性: 键值对{name1: value1} 获取器{ get name(){..} }和 设置器{ set name(val){..}}的计算属性值 var myObject = { myString: 'value 1', get myNumber() { return this._myNumber; }, set myNumber(value) { this._myNumber = Number

  • 浅析Java虚拟机详解之概述、对象生存法则

    Java与C++之间有一堵由内存分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 一.概述 Java堆和方法区这两个区域有着很显著的不确定性: 1.一个接口的多个实现类需要的内存可能会不一样,一个方法所执行的不同条件分支所需要的内存也可能不一样 2.只有处于运行期间,我们才能知道程序究竟会创建哪些对象,创建多少个对象,这部分内存的分配和回收是动态的 垃圾收集器所关注的正是这部分的内存该如何管理 二.对象已死? 1.引用计数法 在对象中添加一个引用计数器,每当有一个地方引用它

  • 详解JVM系列之对象的锁状态和同步

    java对象头 Java的锁状态其实可以分为三种,分别是偏向锁,轻量级锁和重量级锁. 在Java HotSpot VM中,每个对象前面都有一个class指针和一个Mark Word. Mark Word存储了哈希值以及分代年龄和标记位等,通过这些值的变化,JVM可以实现对java对象的不同程度的锁定. 还记得我们之前分享java对象的那张图吗? javaObject对象的对象头大小根据你使用的是32位还是64位的虚拟机的不同,稍有变化.这里我们使用的是64位的虚拟机为例. Object的对象头,

  • 详解Python类和对象内容

    目录 一.什么是Python类? 二.Python类中的方法和属性 2.1.Python类中的方法 2.2.Python类中的属性 三.面向对象的概念 3.1.Python类:继承 3.2.Python类:多态性 3.3.Python类:抽象 一.什么是Python类? python中的类是创建特定对象的蓝图.它使您可以以特定方式构建软件.问题来了,怎么办?类允许我们以一种易于重用的方式对我们的数据和函数进行逻辑分组,并在需要时进行构建.考虑下图. 在第一张图片(A)中,它代表了一个可以被视为C

  • 详解JavaScript中Arguments对象用途

    目录 前言 Arguments 的基本概念 Arguments 的作用 获取实参和形参的个数 修改实参值 改变实参的个数 检测参数合法性 函数的参数个数不确定时,用于访问调用函数的实参值 遍历或访问实参的值 总结 在实际开发中,Arguments 对象非常有用.灵活使用 Arguments 对象,可以提升使用函数的灵活性,增强函数在抽象编程中的适应能力和纠错能力. JavaScript 中 Arguments 对象的用途总结. 前言 相信我们很多人在代码开发的过程中都使用到过一个特殊的对象 --

  • 详解Java内部类与对象的打印概念和流程

    目录 一.内部类的概念 二.内部类的分类 三.成员内部类 1.普通内部类 2.静态内部类 四.局部内部类 五.对象的打印 一.内部类的概念 在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类.内部类也是封装的一种体现. public class OutClass {//外部类 class InnerClass{//内部类 } } 注意事项: 1.内部类一定是定义在class 类名{}之中的类,定义在class 类名{}之外的,哪怕是在一份文件中,也并不

  • 详解Pandas中GroupBy对象的使用

    目录 使用 Groupby 三个步骤 将原始对象拆分为组 按组应用函数 Aggregation Transformation Filtration 整合结果 总结 今天,我们将探讨如何在 Python 的 Pandas 库中创建 GroupBy 对象以及该对象的工作原理.我们将详细了解分组过程的每个步骤,可以将哪些方法应用于 GroupBy 对象上,以及我们可以从中提取哪些有用信息 不要再观望了,一起学起来吧 使用 Groupby 三个步骤 首先我们要知道,任何 groupby 过程都涉及以下

随机推荐