【C++】类和对象

1.类和对象

类的定义

C语言是面向过程的语言,他关注的是过程;C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象的交互完成。
在C++中struct兼容C语言的用法,同时升级成了类。
1.类名就是类型,可以用类名直接定义对象
2.类中可以定义函数
struct既可以定义结构体,也可以定义类,但我们一般使用class来定义类。
类中的内容一般称为类的成员,类中的变量称为类的属性或成员变量,类中的函数称为类的方法或成员函数

类的定义方式:
1.声明和定义都放在类中,成员函数如果在类中定义,可能会被当作内联函数处理
2.函数声明和定义分离,成员函数前需要加上类名::
声明和定义分离可以使代码的可读性,可维护性更强。
在函数中使用变量会先在函数作用域中查找,然后再到全局作用域查找,如果指定类域或命名空间,会到指定域中查找

类的访问限定符

类的访问限定符:
public,private,protected
public修饰的成员可以在类外直接访问。
private和protected修饰的成员在类外不能直接访问。

访问限定符只限制类外访问,类里访问不受限制
class的默认访问权限是private,struct的默认访问权限是public

类的实例化

类的实例化:用类类型创建对象的过程,称为类的实例化。
一个类可以实例化处多个对象,实例化出的对象占用实际的物理空间,存储成员变量。
一个没有成员变量的类,编译器会开一个字节的空间,这个字节没有别的用途,仅仅用来标识这个对象存在。

对象的存储方式

C++采用的方式:对象内只存储成员变量,成员函数存放在公共的代码段。
类的大小的计算方式和结构体的计算方式一致,要注意内存对齐。

结构体的内存对齐:
1 . 第一个成员在与结构体偏移量为0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8
3.结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

为什么要进行内存对齐:
平台原因:不是所有的硬件平台都能访问任意地址的数据的。
性能原因:为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说:
结构体的内存对齐是拿空间来换取时间的做法。

如何让结构体按照指定参数进行对齐:
#pragma pack()

什么是大小端:
小端:数据的低位字节序的内容存放在低地址处,高位字节序的内容存储在高地址处
大端:数据的低位字节序的内容存放在高地址处,高位字节序的内容存储在低地址处

this指针

在形参和实参中,this指针不能显示传递和接收,但在成员函数内部可以使用
this指针类型:类型* const this
this指针本质上是成员函数的形参,所以this指针存储在栈上,一般编译器会对其优化,vs上this指针会被保存在寄存器中。

2.6个默认成员函数

即使是空类,编译器也会生成6个默认成员函数,他们都是特殊的成员函数

构造函数

构造函数是特殊的成员函数,构造函数不是用来创建对象的,而是用来初始化的。
内置类型:编译器自带的类型,像int, double, char, bool,及各种各样的指针类的指针也是内置类型
自定义类型:struct, class, union, enum…

1.函数名和类名相同
2.无返回值(不需要使用void)
3.对象实例化时,自动调用构造函数
4.构造函数可以重载
5.我们不写编译器会自动生成默认无参的构造函数,我们写了就不会生成了
6.默认构造函数:全缺省的构造函数,无参的构造函数,我们不写编译器自动生成的构造函数
默认构造函数特点:不传参数就可以调用

7.编译器生成的默认构造函数,对于内置类型的成员不做处理,自定义类型的成员去调用他的默认构造函数
注意:这里调用的是默认构造函数

C++11中针对内置类型不做处理的缺陷,打了补丁:内置类型成员变量可以在类的声明中给缺省值。
一般的类都不会让编译器默认生成构造函数,都会自己写一个全缺省的默认构造函数。特殊情况下会使用默认生成的构造函数。

析构函数

析构函数不是完成对象的销毁,而是用来完成对象中资源的清理工作。

1.函数名是在类名前加~
2.无参数无返回值 -->无参数所以不能构成函数重载
3.对象的生命周期结束时,自动调用析构函数
4.一个类只能有一个析构函数
5.我们不写编译器会自动生成默认析构函数
6.编译器自动生成的默认析构函数,对内置类型成员不做处理,自定义类型成员去调用他的析构函数

拷贝构造函数

拷贝构造函数是构造函数的一种重载形式

1.拷贝构造函数只有一个形参,且参数必须是类类型对象的引用(一般用const修饰)
2.我们不写编译器会自动生成默认的拷贝构造函数
3.编译器生成的拷贝构造函数,对于内置类型参数进行值拷贝,自定义类型去调用他的拷贝构造函数。
值拷贝:按照字节方式直接拷贝 ,又叫做浅拷贝。

运算符重载

C++为了代码的可读性引入了运算符重载
函数名字为:关键字operator + 需要重载的运算符符号
函数原型:返回值类型 + operator操作符 +(参数列表)

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

前置++和后置++特殊处理,使用重载区分,后置++增加一个int参数和前置++构成重载。

函数重载和运算符重载没有任何关联:
函数重载:在同一作用域下,允许参数不同的同名函数存在
运算符重载:自定义类型可以直接使用运算符

赋值运算符重载

  1. 赋值运算符重载格式
    参数类型:const T&,传递引用可以提高传参效率
    返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
    检测是否自己给自己赋值
    返回*this :要符合连续赋值的含义
  2. 赋值运算符只能重载成类的成员函数不能重载成全局函数,否则会和编译器默认生成的赋值运算符重载冲突
  3. 我们不写编译器自动生成默认的赋值运算符重载
  4. 编译器自动生成的默认赋值运算符重载,对内置类型的成员直接赋值,对自定义类型去调用他的赋值运算符重载赋值。

取地址及const取地址运算符重载

我们不写编译会器自动生成,自动生成的就够用了

const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
在实际使用中,如果函数中不改变成员变量,尽量定义成const成员函数,这样const对象和非const对象都可以调用。

初始化列表

1.以一个冒号开始,接着以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。
2.每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
3.必须使用初始化列表的成员:

1.没有默认构造函数的自定义类型成员
2.const成员变量 -->const必须在定义的时候初始化
3.引用成员变量 -->引用必须在定义的时候初始化

4.尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
初始化列表可以认为是成员变量定义的地方,C++11支持的在声明中给缺省值,就是给的初始化列表
5.成员变量在类中声明次序就是其在初始化列表中的初始化顺序。
总结:能使用初始化列表就使用。

explicit关键字

支持只接收一个参数的构造函数或列表初始化支持隐式类型转换
用explicit修饰构造函数,将会禁止构造函数的隐式转换。

static成员

static成员:static成员变量和static成员函数

class A
{
public:
	A() { ++_count; }
	A(const A& t) { ++_count; }
private:
	static int _count; //声明
	int _aa = 1; //在初始化列表初始化,声明可以给缺省值
};
int A::_count= 0;//类外定义初始化

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

  1. 静态成员函数可以调用非静态成员函数吗?
    不能,没有this指针
  2. 非静态成员函数可以调用类的静态成员函数吗?
    可以,静态成员属于整个类

友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元:友元函数和友元类

友元函数

ostream& operator<<(ostream& out, const Date& d)
{
	cout << d._year << "-" << d._month << "-" << d._day;
	return out;
}

在为Date类实现运算符重载时,会遇到需要访问私有成员的情况,这有几种解决办法:
1.把私有成员设为公有 -->破坏了封装,
2.写一个成员函数,返回所需的成员 -->如果成员太多,不适合使用
3.友元 -->能少使用就少使用,友元破坏了封装
friend inline ostream& operator<<(ostream& out, const Date& d);
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰 const 用来修饰成员函数中的this指针指向的对象
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同

友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
友元关系是单向的。
友元关系不能传递性。

内部类

外部类的大小和内部类的大小没有任何关系。
1.内部类受外部类的类域,访问限定符限制。
2.内部类是外部类的友元类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LRBORRR

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值