类和对象学习笔记

news2024/11/17 5:52:06

类和对象

  • 类的定义
  • this指针
  • 类的6个默认成员函数
    • 构造函数
    • 析构函数
    • 拷贝构造函数
    • 赋值运算符重载
      • 赋值运算符重载
      • 运算符重载
      • const成员
    • 取地址操作符重载
    • const取地址操作符重载
  • 初始化列表
  • explicit关键字
  • static成员
  • 匿名对象
  • 友元
  • 内部类
  • 拷贝对象时编译器的优化

类的定义

c++类的定义形式为:

class className
{
pubilic:
	//...
private:
	//...
};

在类的内部可以定义变量和函数,c++可以通过3个访问限定符来限制类的成员的访问权限:
pubilc:在该作用域的成员在类外可以直接访问
private和protected:在该作用域内的成员在类外不能直接访问
c++为了兼容c,就将c的结构体提升成了类,使用struct和class定义类的唯一区别是:class的成员的默认访问权限为private,struct的成员的默认访问权限是public,其他的并无区别。
在类里面定义的成员函数,编译器会将其当成内联函数来处理,但是否展开,最终还是取决于编译器。我们建议将短小的函数直接在类里面定义,其他的函数则声明和定义分离。
对象是类类型的实例化,类类型是对实例化对象的描述,对于一个类实例化出的多个对象来说,除了成员变量用来存储不一样的数据,成员函数都是一样的,因此为了节省空间,对象只保存成员变量,而成员函数则放在公共的代码段。由此,一个类的大小只需计算成员变量的大小即可,其计算方法与计算结构体的大小的方法一致结构体大小的计算。对于空类,编译器为了标识该类的存在,给了其一个字节的大小。

this指针

c++编译器给每一个非静态成员函数增加了一个隐藏的指针参数,该指针指向当前对象,当对象调用成员函数时就可以通过该指针访问该对象的成员变量,该指针由编译器自行传递,不需要用户来完成,用户可以在类里面显示使用this指针。

//定义了一个类
class Student
{
pubilc:
	void Print()//这里有一个默认this指针,相当于void Print(Student *this)
	{
		cout<<_name<<_age<<_sex;
		//cout<<this->_age,显示使用this指针
	}
private:
	_name[20]="zhangsan";
	_age=20;
	_sex[7]="male"
};

int main()
{
	Student s;
	//调用打印函数打印学生信息
	s.Print();//相当于s.Print(&s)
	
	return 0;
}

this指针特性:
1.this指针的类型为:类类型*const(如Student *const this),因此不能给this指针赋值。
2.this指针只能在成员函数内部使用。
3.this指针本质上是成员函数的形参,所以对象中不存储this指针(同普通函数参数一样,存放在栈区,VS存放在寄存器)。
4.this指针是成员函数参数列表隐藏着的第一个参数。
5.this指针可以为空。

class Student
{
pubilc:
	void Print()
	{
		cout<<“c++;
	}
private:
	_name[20]="zhangsan";
	_age=20;
	_sex[7]="male"
};

int main()
{
	Student* s=nullptr;
	s->Print();
	//由于成员函数不在对象中,此处不需要解引用,故代码可以正常执行
	//此时this指针为空
	
	return 0;
}
class Student
{
pubilc:
	void Print()
	{
		cout<<_age;
	}
private:
	_name[20]="zhangsan";
	_age=20;
	_sex[7]="male"
};

int main()
{
	Student*s=nullptr;
	s->Print();
	//该代码编译通过,但运行崩溃
	
	return 0;
}

类的6个默认成员函数

默认成员函数是当用户没有显式定义时,编译器自动生成的函数。

构造函数

构造函数是名字与类名相同的函数,创建对象时由编译器自动调用,用于给数据成员一个初始值,即初始化对象,该函数在对象整个生命周期内只调用一次。同时构造函数无返回值,可以重载,在调用无参的构造函数时后面不需要跟括号。

class Student
{
pubilc:
	//函数1
	Student(int height,int weight)//用户显式定义构造函数
	{
		_height=height;
		_weight=weight;
	}
	//函数2,无参
	Student()//构成重载
	{
		;
	}
private:
	int _height=170;
	int _weight=60;
};

int main()
{
	Student s1(64,177);//创建时自动调用构造函数1
	Student s2;//创建时自动调用构造函数2
	//s2后面不需要跟括号,即不能写成 Student s2(); 否则就成了函数声明
}

当用户没有显式定义构造函数时,编译器会自动生成一个无参的默认构造函数,该默认构造函数对成员变量的处理方式为:对内置类型成员不做处理,对自定义成员调用其默认构造函数 (默认构造函数只有无参的构造函数、全缺省的构造函数、编译器自动生成的构造函数3种)。

class Date
{
public:
	Date(int year)//用户显式定义了一个参数不是缺省值的构造函数
	{
		_year=year;
	}
private:
	_year;
};

class Student
{
pubilc:
	//用户没有显示定义构造函数,由编译器生成默认构造函数
private:
	int* p;//不处理
	int _height=170;//不处理
	int _weight=60;//不处理
	Date d1;//调用Date类的默认构造函数
	//由于Date类没有默认构造函数,故出错
};

默认构造函数只能有一个

class Date
{
public:
	Date(int year=1)
	{
		cout << "有参数" << endl;
	}

	Date ()
	{
		cout << "无参数" << endl;
	}
private:
	int _year = 60;
	int _month = 0;
	int _day = 0;
};

class Student
{
public:

private:
	int height;
	int weigth;
	Date d;
};

int main()
{
	Date d1(1);//调用有参的构造函数
	Date d2;//对重载的构造函数调用不明确,出错
	Student s;//自定义类型调用其默认构造时不明确,出错
}

由上我们可以得知当成员变量都是内置类型时构造函数可以不写,但大多数情况下都要写构造函数。

析构函数

析构函数的功能与构造函数的功能相反,用于对成员变量的资源的清理,在对象销毁时会自动调用析构函数,析构函数不能重载,参数列表为空。

class Date
{
public:
	Date()
	{
		_s=new int;
	}
	
	~Date()//析构函数
	{
		delete s;//进行资源清理
	}
private:
	int*_s;
}

当用户没有显式定义析构函数时,同构造函数一样,编译器会自动生成一个默认析构函数,该默认析构函数对成员变量的处理方式为:对内置类型成员不做处理,对自定义成员调用其析构函数。

当类里面没有资源申请时,析构函数可以不写,使用编译器生成的默认析构函数就可以了。

拷贝构造函数

用已经存在的类对象创建新对象时会调用拷贝构造函数,如当参数传值为一个类对象、返回一个类对象时等。拷贝构造函数是构造函数的一个重载形式,参数只有一个且必须是类类型对象的引用。

class Date
{
public:
	//参数一定要是引用,如果不是引用,使用拷贝构造函数要进行值拷贝,
	//就会又去调用拷贝构造函数,从而引发无穷递归
	Date(const Date& d)//拷贝构造函数,使用const使代码更健壮
	{
		_year=d._year;
		_month=d._month;
		_day=d._day;
	}
	
private:
	int _year = 60;
	int _month = 0;
	int _day = 0;
};

int main()
{
	Date d1;
	Date d2=d1;//调用拷贝构造函数
}

如果用户没有显式定义拷贝构造函数,默认的拷贝构造函数进行的是浅拷贝(值拷贝),对成员变量的处理方式为:对内置类型成员进行浅拷贝,对自定义成员调用其拷贝构造函数。

如果类里面没有涉及到资源的申请时,拷贝构造函数可以不写,但当涉及到资源申请时,拷贝构造函数一定要写,否则是浅拷贝,容易出错。

class Date
{
public:
	Date()//构造函数
	{
		_s=new int;
	}
	
	~Date()//析构函数
	{
		delete s;//进行资源清理
	}
private:
	int*_s;
}

int main()
{
	Date d1;
	Date d2=d1;//不会再调用构造函数
	//以上代码运行时出错
	//Date类里面进行的是浅拷贝,d1和d2里面的_s指向同一块空间
	//对象d1、d2销毁时都要调用其析构函数,对同一块空间释放了2次,出错

	return 0;
}

赋值运算符重载

赋值运算符重载

当要对一个已经创建好的对象进行赋值操作时,需要调用赋值运算符重载函数

class Date
{
public:
	Date& operator=(const Date& d)//赋值运算符重载
	{
		if(this!=&d)
		{
			_year=d._year;
			_month=d._month;
			_day=d._day;
		}
		return *this;
	}
private:
	_year;
	_month;
	_day;
}

int main()
{
	Date d1;
	Date d2;
	d2=d1;//调用赋值运算符重载
}

该函数要注意以下4点:
1.为了符合连续赋值,函数需要返回*this,同时为了提高返回的效率,需要用到引用返回。
2.为了提升传参效率和增强代码健壮性,参数应为引用且使用const修饰。
3.要检测是否是自己给自己赋值
原因可以参考这里l1dian11的博客
4.赋值运算符只能重载成类的成员函数,不能重载成全局函数。

当用户没有显式定义时,编译器会自动生成一个默认的运算符重载,以值的方式逐字节拷贝,对成员变量的处理方式为:对内置类型成员直接赋值,对自定义成员调用其对应的赋值运算符重载。

运算符重载

c++除了支持赋值运算符重载外,还支持其他的运算符重载,只不过编译器不会自动生成这些运算符重载,需要用户显式定义。
其有以下几点需要注意:
1.只能重载已有的运算符,不能通过其他符号重载新的运算符,如不能重载@
2.重载类型必须有一个类类型参数(防止用户改变该操作符原来的对内置类型的运算)
3.以下5个运算符不能重载:

.*   ::   sizeof   ?:   .

这里说一下比较特殊的运算符重载:

1.前置++和后置++重载
由于这两个运算符的重载无法直接区分,c++进行了特殊处理:在参数列表增加一个int型参数表示后置++

class Date
{
public:
	Date operator++()//表示前置++运算符重载
	{
		//...
	}
	Date operator++(int)//表示后置++运算符重载
	{
		//...
	}
private:
	_year;
	_month;
	_day;
};

2.流插入<<和流提取>>的运算符重载

请添加图片描述

cout是ostream类的对象,cin是istream类的对象

class Date
{
public:
	ostream& operator<<(ostream& out)
	{
		//...
	}
	
private:
	_year;
	_month;
	_day;
};
//用法如下
Date d;
d<<cout;

虽然重载成功了,因为this指针默认占了第一个参数,所以其使用方式很奇怪,不符合我们使用的习惯,因此我们只能将其重载成全局函数。

class Date
{
public:
	//使用友元使类外的函数可以访问类里面的私有成员
	friend ostream& operator<<(ostream& out,const Date& d);
private:
	_year;
	_month;
	_day;
};

friend ostream& operator<<(ostream& out,const Date& d)
{
	//...
}

const成员

大多数情况下我们并不希望成员函数拥有对类里面的成员进行修改的权限,因此我们希望对this指针使用const进行修饰。

class Date
{
public:
	void fun() const
	{
		//...
	}
	以上函数相当于void fun(const Date* const this)
	//第2个const是this指针自带的
private:
	_year;
	_month;
	_day;
};

我们建议只要成员函数不涉及到对成员变量的修改,后面都要加上const进行修饰。

取地址操作符重载

用于对一个普通对象取地址

class Date
{
public:
	Date* operator&()
	{
		return this;//一般是直接返回this即可
		//如果写成return 0x11223344;
		//那么用取地址符获取对象地址时将全都是0x11223344这个地址
	}
private:
	_year;
	_month;
	_day;
};

这个一般不需要重载,使用编译器默认生成的即可。

const取地址操作符重载

用于对const修饰的对象取地址

class Date
{
public:
	const Date* operator&() const
	{
		return this;//一般是直接返回this即可
		//如果写成return 0x11223344;
		//那么用取地址符获取对象地址时将全都是0x11223344这个地址
	}
private:
	_year;
	_month;
	_day;
};

这个一般也不需要重载,使用编译器默认生成的即可。

初始化列表

类在实例化成对象时,所有的成员变量都会在初始化列表中进行定义并给予变量相对应的值,内置类型如果没有显式地写在初始化列表,则会在初始化列表中给予其一个随机值,对自定义类型,会去调用其默认构造函数。初始化列表和构造函数可以混合使用。
c++11打了补丁,允许其在声明时赋值,这些值其实都是缺省值,用于给初始化列表。

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

int main()
{
	Date d(3,3,3);
	//d._year=2,d._month=2,d._day=2;
}

类里面成员变量在类中的声明次序就是初始化列表的初始化顺序,与其在初始化列表中的顺序无关。

explicit关键字

如果类的构造函数只有一个参数或者除第一个参数无默认值其余均有默认值,则该类可以支持隐式转换。

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

int main()
{
	Date d=2019;//_year=2019,_month=1,_day=1
	//将2019转换成Date(2019,1,1),再赋给d
}

c++11还支持多参数的隐式类型转换

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

int main()
{
	Date d={2019,10,11};//_year=2019,_month=10,_day=11
}

有时候我们并不希望这种隐式类型转换的发生,只需在构造函数前面加上explicit关键字即可,但这个关键字不能阻止强制类型转换的发生。

static成员

在类里面以static关键字修饰的成员称为类的静态成员,对于静态成员变量,其只能在类里面进行声明,不能给缺省值。
静态成员有以下特性:
1.静态成员为所有类对象所共享,不属于某个对象,存放在静态区。
2.静态成员变量在类内只是声明,必须要在类外定义,定义时不需加static关键字。
3.静态成员可以直接通过类名::静态成员或者对象.静态成员来访问
4.静态成员没有this指针,不能访问任何非静态成员。
6.静态成员也是类的成员,受public、private、protect访问限定符的限制。

class Date
{
public:

	explicit Date(int year=1,int month=1,int day=1)
	{
		++i;
		_year = year;
		_month = month;
		_day = day;
	}
	static int i;
private:
	int _year;
	int _month;
	int _day;
	
};

int Date::i = 0;

int main()
{
	Date d1;
	Date d2;
	Date d3;
	Date d4;
	Date d5;
	cout << Date::i << endl;//i=5;
	return 0;
}

匿名对象

c++允许匿名对象,可以拥有充当临时变量的作用,其生命周期只在这一行,该行执行完就销毁。

class Date
{
public:

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

int main()
{
	Date d1 = Date(1, 2, 3);//匿名对象
	Date d2 = {1,2,3};//隐式转换

	return 0;
}

友元

友元分为友元函数和友元类
友元函数是定义在类外部的普通函数,不属于任何类,在类里面声明,可以直接访问类的私有成员,其有以下特性:
1.友元函数不能用const修饰
2.友元函数可以定义在类定义的任何地方声明,不受访问限定符的限制
3.一个函数可以是多个类的友元函数
4.友元函数与普通函数的调用原理相同

友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类里面的私有成员。
友元关系是单向的,没有传递性,不能继承。
虽然友元为访问私有成员提供了便利,但破坏了封装,增加了耦合度,不建议过多使用。

内部类

内部类是指定义在另一个类内部的类,他是一个独立的类,不属于外部类,也不能通过外部类的对象访问内部类的成员,可以认为外部类对内部类没有任何优越的访问权限。但内部类却是外部类的友元类,即内部类可以通过类外部的对象参数访问外部类的所有成员。
需要注意外部类的大小和内部类没有任何关系。

拷贝对象时编译器的优化

大部分编译器会对连续的构造或拷贝构造进行优化,如

连续的构造+构造优化为一个构造
连续的构造+拷贝构造优化为一个构造
连续的拷贝构造+拷贝构造优化为一个拷贝构造
不同的编译器的优化方式和程度不同。

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

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

相关文章

leetcode:1773. 统计匹配检索规则的物品数量(python3解法)

难度&#xff1a;简单 给你一个数组 items &#xff0c;其中 items[i] [typei, colori, namei] &#xff0c;描述第 i 件物品的类型、颜色以及名称。 另给你一条由两个字符串 ruleKey 和 ruleValue 表示的检索规则。 如果第 i 件物品能满足下述条件之一&#xff0c;则认为该物…

yum仓库

目录 什么是yum仓库 概念 yum的实现过程 Yum的实现过程如下&#xff1a; yum的配置文件 仓库设置文件 日志文件 yum命令 yum list 显示所有可用的包&#xff0c;包名&#xff0c;支持通配符 yum list*Kernel yum info 显示包的信息 yum search ftp 查…

Linux应用开发基础知识——网络通信编程(九)

前言&#xff1a; 通过学习为后续Linux网络编程奠定基础。首先介绍网络编程的概念&#xff0c;即网络协议分层&#xff0c;旨在帮助读者对网络建立初步的、全面立体的认识&#xff0c;其次介绍包括协议、端口、地址等&#xff1b;最后介绍应用非常广泛的传输控制协议&#xff0…

口袋参谋:找关键词的三种方法!

​如何找热搜关键词&#xff1f;99%的商家都不知道。那么今天可以根据我说的三种方法去做。 第一种方法&#xff1a;利用竞争对手 通过分析竞争对手&#xff0c;正在使用和采取何种优化方法&#xff0c;来帮助你理解市场上正在流行什么样的关键字&#xff0c;这些热词可以直接从…

[Mac软件]Downie 4.6.34视频下载工具

以下是关于Downie软件的介绍&#xff1a; Downie是一款非常实用的视频下载软件&#xff0c;专门为Mac用户设计。这款软件的使用方法非常简单&#xff0c;只需要将想要下载的视频链接复制到Downie的界面&#xff0c;它就能够自动下载。 Downie最大的特点就是支持的网站非常多&a…

labelImg

labelImg 在anaconda虚拟环境中安装labelImg 进入conda虚拟环境DL2中 输入命令&#xff1a; pip install PyQt5 pip install pyqt5-tools pip install lxml pip install labelimg PyQt5:是用于创建GUI应用程序的跨平台工具包&#xff0c;它将Python与Qt库融为一体 Lxml&#…

重磅解读 | 阿里云 云网络领域关键技术创新

云布道师 10 月 31 日&#xff0c;杭州云栖大会&#xff0c;阿里云技术主论坛带来了一场关于阿里云主力产品与技术创新的深度解读&#xff0c;阿里云网络产品线负责人祝顺民带来《云智创新&#xff0c;网络随行》的主题发言&#xff0c;针对阿里云飞天洛神云网络&#xff08;下…

CentOS8部署Skywalking(非容器方式)

一、官网下载安装包 二、安装 #tar -zxf apache-skywalking-apm-9.6.0.tar.gz #mv apache-skywalking-apm-9.6.0 skywalking #cd /opt/skywalking 修改配置文件 #vi /opt/skywalking/config/application.yml #vi vi /opt/skywalking/webapp/application.yml 三、运行 ./bin…

inBuilder低代码平台新特性推荐-第九期

各位知乎的友友们&#xff0c;大家好~ 今天来给大家带来的是inBuilder低代码平台特性推荐系列第九期——子表弹出新增&#xff01; 01 概述 子表弹出新增&#xff0c;是低代码平台提供的一种前端输入组件&#xff0c;在子表字段较多的场景中&#xff0c;有时为了方便…

【Linux】 find命令使用

find find命令是一种通过条件匹配在指定目录下查找对应文件或者目录的工具。匹配的条件可以是文件名称、类型、大小、权限属性、时间戳等。find命令还可以配合相关命令对匹配到的文件作出后续处理。 语法 find [路径...] [表达式] [path...]为需要查找文件所指定的路径。如果…

【GUI】-- 12 贪吃蛇小游戏之让小蛇动起来

GUI编程 04 贪吃蛇小游戏 4.3 第三步&#xff1a;让小蛇动起来(键盘控制) 首先&#xff0c;在构造器中要获取焦点事件、键盘监听事件并加入定时器(定时器定义需要实现ActionListener接口并重写actionPerformed方法)&#xff1a; //构造器public GamePanel() {init();this.s…

后端-锁专题:synchronized(java对象结构、锁的类型、锁升级流程...)

文章目录 对象的结构以及大小内存换算java的常见数据类型以及所占字节数分析对象总共占多少字节&#xff0c;各项占多少字节对象头结构 锁类型锁升级流程 对象的结构以及大小内存换算 java的常见数据类型以及所占字节数 String&#xff1a;8字节 64位 int&#xff1a;4字节 …

虾皮选品免费工具:如何用知虾进行虾皮市场分析选品

在如今的电商时代&#xff0c;了解市场需求和选择热销产品是成功经营的关键。虾皮作为东南亚地区最大的电商平台之一&#xff0c;提供了一系列的选品工具&#xff0c;帮助卖家在市场竞争中脱颖而出。本文将介绍如何使用虾皮的免费工具——知虾进行虾皮市场分析选品&#xff0c;…

MS90C386:+3.3V 175MHz 的 24bit 平板显示器(FPD)LVDS 信号接收器

产品简述 MS90C386 芯片能够将 4 通道的低压差分信号&#xff08; LVDS &#xff09;转换成 28bit 的 TTL 数据。时钟通道与数据通道并行输入。在时钟频率 为 175MHz 时&#xff0c; 24bit 的 RGB 数据、 3bit 的 LCD 时序数据和 1bit 的控制数据以 1225Mb…

竞赛 题目:基于深度学习的图像风格迁移 - [ 卷积神经网络 机器视觉 ]

文章目录 0 简介1 VGG网络2 风格迁移3 内容损失4 风格损失5 主代码实现6 迁移模型实现7 效果展示8 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习卷积神经网络的花卉识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c…

真菌DAP-seq|丝状真菌中与碳利用相关的调控和转录景观

转录因子 (Transcription Factors, TFs)是指能够以序列特异性方式结合DNA并且调节转录的蛋白质。TF与特异性DNA序列结合调节转录&#xff0c;同时会和其它功能蛋白结合调控下游基因的转录和翻译过程&#xff0c;也会和增强子等其它顺式作用元件结合&#xff0c;使整个调控过程更…

API网关那些事【架构新知系列】

目前随着云原生ServiceMesh和微服务架构的不断演进&#xff0c;网关领域新产品不断出现&#xff0c;各种网关使用的技术&#xff0c;功能和应用领域也不断扩展&#xff0c;在各有所长的前提下也有很多功能重合&#xff0c;网上各种技术PR文章&#xff0c;评测资料和网关落地实践…

淘宝商品详情接口,商品属性接口,商品信息查询,商品详细信息接口,h5详情,淘宝APP详情

淘宝商品详情API接口可以使用淘宝开放平台提供的SDK或API来获取。这些接口可以用于获取商品的详细信息&#xff0c;如标题、价格、描述、图片等。 以下是使用淘宝开放平台API获取商品详情的步骤&#xff1a; 注册淘宝开放平台账号&#xff0c;并创建应用&#xff0c;获取应用…

VR全景打造亮眼吸睛创意内容:三维模型、实景建模

随着VR技术在不同行业之间应用落地&#xff0c;市场规模也在快速扩大&#xff0c;VR全景这种全新的视觉体验为我们生活中的许多方面都带来了无限的可能。更加完整的呈现出一个场景或是物体的所有细节&#xff0c;让浏览者感受到自己仿佛置身于现场一般&#xff1b;其次&#xf…

参与活动如何进行地区的限制

对活动地区限制分为两步&#xff1a;一是管理端配置&#xff0c;而是移动端限制 移动端限制 使用高德获取经纬度&#xff08;需要引入高德库&#xff1a;https://webapi.amap.com/maps&#xff09;&#xff0c;如果是app也可以调用jsapi获取经纬度 export const checkAppPermis…