类和对象学习笔记

类和对象

  • 类的定义
  • 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.友元函数与普通函数的调用原理相同

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

内部类

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

拷贝对象时编译器的优化

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MATLAB基础绘图第17棒】绘制玫瑰图

MATLAB绘制玫瑰图 玫瑰图&#xff08;Nightingale Rose Chart&#xff09;风玫瑰图&#xff08;WindRose&#xff09;准备工作&#xff1a;WindRose工具包下载案例案例1&#xff1a;基础绘图 参考 玫瑰图&#xff08;Nightingale Rose Chart&#xff09; 玫瑰图&#xff08;Ni…

G管螺纹尺寸对照表

G管螺纹尺寸对照表 NPT 是 National (American) Pipe Thread 的缩写&#xff0c;属于美国标准的 60 度锥管螺纹&#xff0c;用于北美地区&#xff0e;国家标准可查阅 GB/T12716-1991 PT 是 Pipe Thread 的缩写&#xff0c;是 55 度密封圆锥管螺纹&#xff0c;属惠氏螺纹家族&a…

【算法设计实验三】动态规划解决01背包问题

请勿原模原样复制&#xff01; 01背包dp具体解释详见链接 ↓ 【算法5.1】背包问题 - 01背包 &#xff08;至多最大价值、至少最小价值&#xff09;_背包问题求最小价值_Roye_ack的博客-CSDN博客 关于如何求出最优物品选择方案&#xff1f; 先在递归求dp公式时&#xff0c;若…

黑马React18: 基础Part II

黑马React: 基础2 Date: November 16, 2023 Sum: 受控表单绑定、获取DOM、组件通信、useEffect、Hook、优化B站评论 受控表单绑定 受控表单绑定 概念&#xff1a;使用React组件的状态&#xff08;useState&#xff09;控制表单的状态 准备一个React状态值 const [value, se…

YOLOV5 C++部署的人员检测项目【学习笔记(十一)】

本文为修改后的转载&#xff0c;没有转载链接&#xff0c;所以文章类型暂为原创 文章目录 一、安装Pytorch 及 YOLO v51.1 安装GPU版 pytorch1.2 安装YOLO v5所需依赖 二、YOLO v5训练自定义数据2.1 标注数据2.1.1 安装labelImg2.1.2 标注 2.2 准备数据集2.2.1 组织目录结构2.…

dvwa-command injection 代码审计(超详细逐行审计)

dvwa-command injection 代码审计 low <?phpif( isset( $_POST[ Submit ] ) ) {// Get input$target $_REQUEST[ ip ];// Determine OS and execute the ping command.if( stristr( php_uname( s ), Windows NT ) ) {// Windows$cmd shell_exec( ping . $target );}…

姓氏情侣家庭亲子谐音顽梗头像分销流量主微信抖音小程序开发

姓氏情侣家庭亲子谐音顽梗头像分销流量主微信抖音小程序开发 姓氏情侣头像&#xff1a;提供各种姓氏的情侣头像模板&#xff0c;用户可根据自己的姓氏选择合适的头像进行定制。 家庭头像&#xff1a;为家庭成员提供多种形式的头像模板&#xff0c;让用户可以选择合适的家庭头像…

使用Arrays.asList与不使用的区别

在写算法的时候&#xff0c;遇到了有的题解使用的是Arrays.asList&#xff0c;也有的是直接新建一个List集合将元素加进去的。 看了一下算法的时间&#xff0c;两者居然相差了9秒。 算法原地址&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长…

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

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

Google Chrome 任意文件读取 (CVE-2023-4357)漏洞

漏洞描述 该漏洞的存在是由于 Google Chrome 中用户提供的 XML 输入验证不足。远程攻击者可以创建特制网页&#xff0c;诱骗受害者访问该网页并获取用户系统上的敏感信息。远程攻击者可利用该漏洞通过构建的 HTML 页面绕过文件访问限制&#xff0c;导致chrome任意文件读取。Li…

捷诚管理信息系统 SQL注入漏洞复现

0x01 产品简介 捷诚管理信息系统是一款功能全面&#xff0c;可以支持自营、联营到外柜租赁的管理&#xff0c;其自身带工作流管理工具&#xff0c;能够帮助企业有效的开展内部审批工作。 0x02 漏洞概述 捷诚管理信息系统CWSFinanceCommon.asmx接口存在SQL注入漏洞。未经身份认…

<C++> 模板-下

目录 前言 一、非类型模板参数 二、类模板的特化 1. 概念 2. 函数模板特化 3. 类模板特化 4. 全特化 5. 偏特化 5.1 特化部分参数 5.2 对某些类型的进一步限制 三、模板的分离编译 1. 概念 2. 分离编译 3. 解决方法 1. 显式实例化 2. 在一个文件内写声明和定义 四、模板总结 …

基于nodejs学校宿舍管理系统-计算机毕设 附源码45118

nodejs学校宿舍管理系统 摘要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对学校宿舍管理系统等…

Java_异常详解

前言 异常是什么,异常如何抛出,如何抛出自定义异常,异常处理主要的五个关键字&#xff1a;throw,try,catch,finally,throws ,异常的处理流程 异常是什么 在Java中&#xff0c;将程序执行过程中发生的不正常行为称为异常。比如之前写代码时经常遇到的&#xff1a; 1. 算数异…

PySide6 Tutorials (一)表格小部件魔改

前言 Pyside6官方教程给了一个使用表格显示颜色的教程&#xff0c;原教程地址如下&#xff1a;源地址&#xff0c; 结合前面button信号的学习&#xff0c;就魔改添加了如下功能&#xff1a;增加一列按钮&#xff0c;可以修改该行的颜色值&#xff0c;通过点击按钮生成指定的颜…

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

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

uniapp中swiper 轮播带左右箭头,点击切换轮播效果demo(整理)

可以点击箭头左右切换-进行轮播 <template><view class"swiper-container"><swiper class"swiper" :current"currentIndex" :autoplay"true" interval"9000" circular indicator-dotschange"handleSw…

golang指针学习

package mainimport "fmt"func main() {name:"飞雪无情"nameP:&name//取地址fmt.Println("name变量的内存地址为:",&name)fmt.Println("name变量的值为:",name)fmt.Println("name变量的内存地址为:",nameP)fmt.Prin…

中大型企业网搭建(毕设类型)

毕业设计类别 某大学网络规划与部署 目录 某大学网络规划与部署 第一章项目概述 1.1 项目背景 1.2 网络需求分析 第二章网络总体设计方案 2.1 网络整体架构 2.2 网络设计思路 第三章 网络技术应用 3.1 DHCP 3.2 MSTP 3.3 VRRP 3.4 OSPF 3.5 VLAN 3.6 NAT 3.7 WLAN 3…

完美解决:yum -y install nginx 报出 没有可用软件包 nginx。错误:无须任何处理

目录 一、问题&#xff1a; 二、原因&#xff1a; 三、解决方法&#xff1a; 一、问题&#xff1a; [rootlocalhost ~]# yum -y install nginx 已加载插件&#xff1a;fastestmirror Loading mirror speeds from cached hostfile * base: mirrors.bfsu.edu.cn * extras: m…