【高级程序设计语言C++】类与对象

news2024/11/18 8:32:57

  • 2.1类的定义
    • 2.1.1 类的两种定义方式
    • 2.1.2 类的访问限定符
    • 2.1.3 C++中的struct和class的区别是什么?
    • 2.1.4 类的实例化
    • 2.1.5 计算类对象的大小
    • 2.1.6 this指针
  • 2.2 类的6个默认成员函数
    • 2.2.1 构造函数
    • 2.2.2 析构函数
    • 2.2.3 拷贝构造函数
    • 2.2.4 赋值运算符重载
    • 2.2.5 取地址及const取地址操作符重载
  • 2.3 初始化列表
  • 2.4 static成员

2.1类的定义

C++提供了一种比结构体类型更安全有效的的数据类型-----类。

class为定义类的关键字,ClassName为类的名字,{} 中为类的主体,注意类定义结束时后面分 号不能省略。

class className
{
	// 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号

类体中内容称为 类的成员:类中的变量称为 类的属性或成员变量; 类中的函数称为 类的方法或者成员函数

类是一种数据类型,它是一种用户定义的抽象的数据类型。类代表的是一批对象的共性和特征。类是对象的抽象,而对象是类的实例。 如同结构体类型和结构体变量的关系一样。

需要注意的是,类中的数据成员可以是任何数据类型,但是不能用auto、register和extern说明。

2.1.1 类的两种定义方式

  1. 声声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
class student {
public:
	void showinfo() {
		cout << name << " " << age << " " << classid << " " << score << endl;
	}
private:
	string name;
	int age;
	int classid;
	int score;
};
  1. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
class student {
public:
	void showinfo();
private:
	string name;
	int age;
	int classid;
	int score;

};
void student::showinfo() {
	cout << name << " " << age << " " << classid << " " << score << endl;
}

2.1.2 类的访问限定符

类的访问限定符有3个,分别是public(公有), private(私有),protected(保护)

他们的说明如下:

  1. public修饰的成员在类外可以直接被访问
  2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. 如果后面没有访问限定符,作用域就到} 即类结束。
  5. class的默认访问权限为private,struct为public(因为struct要兼容C)

2.1.3 C++中的struct和class的区别是什么?

C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来

定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类

默认访问权限是private

2.1.4 类的实例化

用类类型创建对象的过程,称为实例化。

例如上面的student类,里面有成员变量和成员函数,但是这些只是一个类,真正用这个类创建一个对象的时候,就叫做类的实例化。就好像学校给了一个表,这个表叫做个人信息表(类),然后你写上你的名字(对象名),然后填写完整信息(对象的使用)。

一个类是可以实例化多个对象的,就好像一家公司有着一张设计图,但是实际设计每个人都是不同的,设计出来的房子也是不同的,而设计出来的房子就是实例化的对象。

2.1.5 计算类对象的大小

c++中对于类对象的大小的计算其实很简单,类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

2.1.6 this指针

当定义了一个类的若干对象,系统会为每一个对象分配存储空间。如果一个类包含数据成员和成员函数,就要分别为数据和函数的代码分配空间。按照以上思路,如果用一个类定义了5个对象,那么就应该分别为这5个对象的数据和函数代码分配存储空间。

实际上,给对象赋值就是给对象的数据成员赋值,不同对象的存储单元中存放的数据值通常是不相同的,但是不同对象的函数代码是相同的,不论调用哪一个对象的成员函数,其实调用的都是相同内容的代码段。因此没有必要为每一个对象都开辟存储成员函数的空间。

C++编译系统只用了一段空间来存放这个共同的函数代码段,在调用各对象的成员函数时,都去调用这个公用的函数代码。因此,每个对象的存储空间都只是该对象数据成员所占用的存储空间,而不包括成员函数所占用的空间,函数代码是存储在对象空间之外的。

每个对象都有属于自己的数据成员,但是所有对象的成员函数代码却共用一份,那么成员函数是怎么辨别出当前调用自己的是哪个对象呢?

C++编译器给每个“非静态的成员函数“增加了一个隐藏 的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量” 的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编 译器自动完成。

对于以上的理解,你可以这么来理解。一个类相当于一个小区,小区里会为每个住户(数据成员)都提供了空间,并且这个小区有很多的运动场地(成员函数)。运动场地和住户的空间肯定是分开的,并且每个住户都是不同的,但是运动场地是相同的,所以就没必要说为每一个住户都提供运动场地,小区只需要为住户提供公共场地即可。

小区提前为住户提供了空间,而这个空间对应来说就是门牌号,也就是地址嘛。那么小区同时给每个住户提供钥匙,用来开启运动场地的大门,假设这个运动场地是智能,当用户一开门,就会知道是哪个用户开的门,也就类似C++的this指针。

2.2 类的6个默认成员函数

一个类中啥都没有,那么就称这个类为空类。那么问题来了,空类就是上面都没有吗?

并不是这样的,一个类中如果什么都没有,那么编译器会自动生成以下6个默认成员函数。

class student {
	
}
  1. 构造函数,主要完成初始化工作
  2. 析构函数,主要完成清理工作
  3. 拷贝构造函数,是使用同类对象初始化创建对象
  4. 赋值运算符重载,主要是把一个对象赋值给另一个对象
  5. 取地址运算符重载
  6. const取地址运算符重载

2.2.1 构造函数

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证

每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

class student {
public:
	void showinfo() {
		cout << _name << "___" << _age << "___" << _classid << "___" << _score << endl;
	}
private:
	string _name;
	int _age;
	int _classid;
	int _score;
};

int main() {
	student s;
	s.showinfo();
	return 0;
}

输出结果

img

如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

在student类中,我没有显式定义构造函数,也并没有对类成员变量进行任何的初始化,**因此对于string类型,初始值是空串(“”),int类型是随机值,**但是在创建类对象s的时候,编译器自动调用了student类的默认是构造函数。

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。

构造函数的特征如下:

  1. 函数名与类名相同。
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载
class student {
public:
	void showinfo() {
		cout << _name << "___" << _age << "___" << _classid << "___" << _score << endl;
	}
	student(string name, int age, int classid, int score) {
		_name = name;
		_age = age;
		_classid = classid;
		_score = score;
	}
private:
	string _name;
	int _age;
	int _classid;
	int _score;
};

int main() {
	student s("张三",18,101,95);
	s.showinfo();
	return 0;
}

输出结果:

img

从上面的代码看,构造函数似乎没有创建的必要。但是这只是片面的想法,一个类中不可能只有简单的内置类型,对于自定义的类,可能也会含有自定义类的成员变量,所以构造函数很有必要。

在c++中对于自定义类型和内置类型,构造函数是有区别对待的。对于内置类型,不做初始化处理。而自定义类型,则会去调用它的默认构造函数。

对于内置类型不初始化的缺陷,c++11中给出了补丁补偿,内置类型成员变量在类中声明时给默认值。

class student {
public:
	void showinfo() {
		cout << _name << "___" << _age << "___" << _classid << "___" << _score << endl;
	}
	student(string name, int age, int classid, int score) {
		_name = name;
		_age = age;
		_classid = classid;
		_score = score;
	}
	student() {

	}
private:
	string _name = "";
	int _age = 0;
	int _classid = 0;
	int _score = 0;
};

int main() {
	student s1("张三",18,101,95);
	student s2;
	s1.showinfo();
	s2.showinfo();
	return 0;
}

输出结果:

img

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。

注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为

是默认构造函数。

2.2.2 析构函数

事物都是从有到无的,那么对于一个对象来说,它是怎么嘎的呢?

析构函数便是让一个对象嘎了的存在。

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由

编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

特征如下:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

同样的,析构函数的存在是为了应付对象的成员变量中含有自定义类的情况。对于内置类型,只需要系统将其内存回收即可,但是对于自定义类,就要调用它的析构函数来完成回收任务。

class worker {
public:
	~worker() {
		cout << "~worker" << endl;
	}
private:
	string _name;
	int workid;
};
class factory {
private:
	worker _worker;
	string _name;
};
int main() {
	factory f;
	return 0;
}

输出结果:

img

对于factory中的内置类型,由操作系统直接将其回收,但是factory中含有一个自定义类的对象,那么就要调用这个自定义类的对象的析构函数来完成回收任务。

2.2.3 拷贝构造函数

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

主要特征

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

\2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

关于第二点特征,解释如下:

img

首先编译器会报错,其次从使用的角度来看会引发无穷调用

img

对于没有显示定义拷贝构造函数的类,将会由编译器生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的

下面来看一个例子:

class my_array {
public:
	void print() {
		for (int i = 0; i < 5; i++) {
			cout << a[i] << " ";
		}
		cout << endl;
	}
	~my_array() {
		free(a);
	}
private:
	int a[5] = {1,2,3,4,5};
};
void test() {
	my_array arr1;
	arr1.print();
	my_array arr2(arr1);
	arr2.print();
}
int main() {
	test();
	return 0;
}

运行结果:

img

为什么会这样子呢?在上面提到,如果在类中未显示定义拷贝构造函数,那么就会由编译器自动生成默认的拷贝构造函数。所以上面的例子中,类里面有一个数组,默认的拷贝构造函数造成了浅拷贝,如下图所示。

img

也就是说arr2的a数组和arr1的a数组是同一块内存空间,但是arr2先走析构函数,那么就会造成这一块共同空间已经被释放了,对于arr1来说并不知道,所以当arr1再次释放a数组时就产生了崩溃。

拷贝构造函数典型调用场景:

  1. 使用已存在对象创建新对象
  2. 函数参数类型为类类型对象
  3. 函数返回值类型为类类型对象

2.2.4 赋值运算符重载

参数类型:const T&,传递引用可以提高传参效率

返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值检测是否自己给自己赋值

返回*this :要复合连续赋值的含义

class student {
public:
	void showinfo() {
		cout << _name << "___" << _age << "___" << _classid << "___" << _score << endl;
	}
	student(string name, int age, int classid, int score) {
		_name = name;
		_age = age;
		_classid = classid;
		_score = score;
	}
	student& operator=(const student& s) {
		if (this != &s) {
			_name = s._name;
			_age = s._age;
			_classid = s._classid;
			_score = s._score;
		}
		return *this;
	}
private:
	string _name = "";
	int _age = 0;
	int _classid = 0;
	int _score = 0;
};
int main() {
	student s1("张三", 18, 101, 95);
	student s2 = s1;
	s1.showinfo();
	s2.showinfo();
	return 0;
}

输出结果:

img

赋值运算符只能重载成类的成员函数不能重载成全局函数

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

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

可以参考上面的拷贝构造函数。

2.2.5 取地址及const取地址操作符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。这里就不过多赘述了。

2.3 初始化列表

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

一个放在括号中的初始值或表达式。

class student {
public:
	void showinfo() {
		cout << _name << "___" << _age << "___" << _classid << "___" << _score << endl;
	}
	student(string name, int age, int classid, int score)
		:_name(name),_age(age),_classid(classid),_score(score)
	{}
private:
	string _name = "";
	int _age = 0;
	int _classid = 0;
	int _score = 0;
};
int main() {
	student s1("张三", 18, 101, 95);
	s1.showinfo();
	return 0;
}

注意:

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
  • 引用成员变量
  • const成员变量
  • 自定义类型成员(且该类没有默认构造函数时)
  1. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
  2. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。

2.4 static成员

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。

主要特性:

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

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

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

相关文章

Java新特性:Lambda表达式

Java新特性&#xff1a;Lambda表达式 Lambda 表达式&#xff08;Lambda expression&#xff09;&#xff0c;也可称为闭包&#xff08;Closure&#xff09;&#xff0c;是 Java&#xff08;SE&#xff09;8 中一个重要的新特性。Lambda 表达式允许我们通过表达式来代替功能接口…

第五章——循环和关系表达式

for循环 很多情况下都需要程序执行重复的任务 #include<iostream> using namespace std; int main() {int i;for (i 0; i < 5; i){cout << "C knows loop.\n";}cout << "C knows when to stop.\n";return 0; } for循环的组成部分 …

107、基于51单片机多路无线调频对讲机系统设计(程序+原理图+PCB源文件+参考论文+外文翻译+任务书+开题报告+硬件设计资料+元器件清单等)

摘 要 对讲机作为短距离通信和移动调度指挥的重要工具,在社会各个行业都有广泛的应用。尤其是随着数字电路技术的发展&#xff0c;新型的对讲机无论在外型还是性能上相对传统的模拟对讲机都有了长足的进步。对讲机主要包含需要基站支持的集群对讲机和常规无中心对讲机两种&…

深度理解 JAVA序列化

前言 相信大家日常开发中&#xff0c;经常看到Java对象“implements Serializable”。那么&#xff0c;它到底有什么用呢&#xff1f;本文从以下几个角度来解析序列这一块知识点~ 什么是Java序列化&#xff1f;为什么需要序列化&#xff1f;序列化用途Java序列化常用API序列化…

Android 热修复一

一、什么是热修复&#xff1f; 在我们应用上线后出现bug需要及时修复时&#xff0c;不用再发新的安装包&#xff0c;只需要发布补丁包&#xff0c;在客户无感知下修复掉bug。 实现效果&#xff1a; Demo源码&#xff1a; https://gitee.com/sziitjim/hotfix 二、怎么进行热修…

极速上手k8s,Kubernetes 从入门到摸鱼系列-理论篇

1. 引言&#x1f44b; 大家好&#xff0c;我是比特桃&#xff01;随着微服务架构越来越流行&#xff0c;大规模的微服务容器编排成了一件具有挑战的事情。在这次容器化云原生的发展中&#xff0c;Docker 成了容器化的赢家&#xff0c;而 Kubernetes 则成为了容器编排的赢家。k…

「已解决」 模块““umi“” ““@umijs/max“” 没有导出的成员“useRequest” “request” 问题的所有方法汇总

背景 使用 Umi 搭建项目时候有的时候会出现这种错误&#xff0c;模块““umi”” ““umijs/max”” 没有导出的成员“useRequest” “request”。 解决 tsconfig.json "paths": {"/*": ["src/*"],"/*": ["./src/.umi/*"…

用JShaman本地部署版,加密2.7MB的Webpack生成的JS文件

JShaman是知名的JS代码保护平台。在线使用&#xff0c;一键混淆加密&#xff0c;无需注册、无需登录。可免费用&#xff0c;也有商业服务&#xff1b;有在线使用的SAAS平台网站&#xff0c;也有本地部署版。很方便、很强大&#xff0c;很专业。 今天&#xff0c;测试使用JSham…

unordered_map模拟实现|STL源码剖析系列|开散列

博主很久没有更新过STL源码剖析这个系列的文章了&#xff0c;主要是因为大部分STL常用的容器&#xff0c;博主都已经发过文章了&#xff0c;今天博主带着大家把哈希表也模拟实现一下。 前言 那么这里博主先安利一下一些干货满满的专栏啦&#xff01; 手撕数据结构https://blo…

点云最小外包矩形计算

1、原理介绍 一簇点云的最小外包矩形&#xff08;Minimum Bounding Rectangle&#xff0c;MBR&#xff09;&#xff0c;是指用一个矩形将该簇点云框起来&#xff0c;所有点云数据在矩形框内。如下图所示为一个矩形框刚好将点云数据全部包围。 下面给出一种基于最大重叠度的最小…

[工业互联-19]:如何在QT中增加SOEM主站

目录 第1章 基本步骤 第2章 详细步骤 2.1.QT安装 2.2.VS安装 2.3.Win10 Debuggers 2.4.QT配置 2.5. SOEM移植 &#xff08;&#xff11;&#xff09;lib库生成 &#xff08;2&#xff09;文件移植: 文件整理 第1章 基本步骤 要在QT中添加SOEM主站功能&#xff0c;您需…

用OpenCV创建一张灰度黑色图像并设置某一列为白色

这段代码首先创建了一个400行600列的单通道灰度图像。然后,它遍历图像中的每个像素。如果像素位于列索引为30的列中,则将该像素的值设置为255。在灰度图像中,0表示黑色,255表示白色。因此,这段代码将图像的第30列设置为白色。 在 OpenCV 中,cv::Mat 构造函数的调用 cv::…

【算法 -- LeetCode】(13)罗马数字转整数

1、题目 罗马数字包含以下七种字符&#xff1a; I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M …

哈希表和字符串专题1—205. 同构字符串 1002. 查找共用字符 925. 长按键入 844.比较含退格的字符串 C++实现

文章目录 205. 同构字符串1002. 查找共用字符925. 长按键入844.比较含退格的字符串栈模拟双指针 205. 同构字符串 class Solution { public:bool isIsomorphic(string s, string t) {unordered_map<char, char> map1;unordered_map<char, char> map2;for(int i0, j…

AI绘画:StableDiffusion炼丹Lora攻略-实战萌宠图片生成

Lora攻略-实战萌宠图片生成 写在前面的话一&#xff1a;准备二、Lora作用1.AI模特2.炼衣服Lora3.改变画风/画面背景Lora模型究竟是什么&#xff1f; 三、如何炼制自己的Lora模型&#xff1f;四、炼丹前的准备&#xff08;**下载整合包**&#xff09;五、选择合适的大模型六、高…

管理类联考——逻辑——记忆篇——数字编码——公式

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;考取过HCIE Cloud Computing、CCIE Security、CISP、RHCE、CCNP RS、PEST 3等证书。&#x1f433; &#x1f495;兴趣爱好&#xff1a;b站天天刷&…

MySQL练习题(2)

创建如下员工标表 插入数据 1-- 按员工编号升序排列不在10号部门工作的员工信息 2-- 查询姓名第二个字母不是A且薪水大于1000元的员工信息&#xff0c;按薪水降序排列 4-- 求每个部门的平均薪水 5-- 求每个部门的最高薪水 6-- 求每个部门…

Coverity 2021.9 for win Coverity 2022.6 for linux

Coverity是一款快速、准确且高度可扩展的静态分析 (SAST) 解决方案&#xff0c;可帮助开发和安全团队在软件开发生命周期 (SDLC) 的早期解决安全和质量缺陷&#xff0c;跟踪和管理整个应用组合的风险&#xff0c;并确保符合安全和编码标准。Coverity 是一款精确的综合静态分析与…

JVM04-优化JVM内存分配以及内存持续上升问题和CPU过高问题排查

1-JVM内存分配 1.1-JVM内存分配性能问题 JVM内存分配不合理最直接的表现就是频繁的GC&#xff0c;这会导致上下文切换等性能问题&#xff0c;从而降低系统的吞吐量、增加系统的响应时间。因此&#xff0c;如果你在线上环境或性能测试时&#xff0c;发现频繁的GC&#xff0c;且…

密码学入门——分组密码模式

文章目录 参考书一、简介二、ECB模式三、CBC模式四、CFB模式五、OFB模式六、CTR模式 参考书 图解密码技术&#xff0c;第三版 一、简介 分组密码工作模式指的是将明文分成固定长度的块&#xff0c;并对每个块应用相同的加密算法进行加密的过程。这些块的长度通常是64位或128…