C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
在C++中,结构体内不仅可以定义变量,也可以定义函数。
类的两种定义方式
- 声明和定义全部放在类体中
- .声明放在.h文件中,类的定义放在.cpp文件中
访问限定符说明
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- class的默认访问权限为private,struct为public(因为struct要兼容C)
C++中struct也可以定义类,和class是一样的。
区别是struct的成员默认访问是public,class的成员默认访问是private。
封装
面向对象的三大特性是:封装、继承、多态
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象交互。
类对象的存储方式
一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐(就和计算结构体的大小一样)注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。
this指针
C++中通过引入this指针解决该问题,即:C++编译器给每个“成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象 如:Date d , d.display() ),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
this指针的特性
1. this指针的类型:类的类型* const (即不能改变this指向,但是可以改变 值)
2. 只能在“成员函数”的内部使用
3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
4. this指针是成员函数第一个隐含的指针形参,一般由编译器通过ecx寄存器自动传递,不需要用户传递
构造函数
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次。
构造函数是特殊的**成员函数**,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。(无参构造函数只能有一个)
默认构造函数是为了在另一个类中有一个类类型的成员时可以初始化成功
析构函数
与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值。
3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
析构函数不销毁对象!只是在销毁对象之前完成一些清理工作!如Date d;d.p = (int)malloc(sizeof(int)); 析构函数会把这个在堆上生成的空间释放掉。但是d任然存在!*
拷贝构造函数
只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。(因为会不停的给形参赋值)(用const的原因:有时会用临时的或者说匿名变量来拷贝构造)
3.若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。
String s1("hello");
String s2(s1);//这样拷贝,是将s1和s2指向同一个堆地址,前一个s1的寿命结束会把这个堆空间释放掉,然后后面的s2寿命结束时就会发生二次释放,导致错误
赋值运算符重载
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型或者枚举类型的操作数
3.用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4.作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默 认的形参this,限定为第一个形参
5. .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。
赋值运算符主要有四点:
7. 参数类型
8. 返回值
9. 检测是否自己给自己赋值
10. 返回*this(为了能够连续赋值,如:Date d1,d2;d1 = d2 = 2008)
11. 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。
const成员
将const修饰的类成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
如:void Display( ) const —> void Display(const Date* this)
const对象 不可以调用 非const成员函数
非const对象 可以调用 const成员函数 (权限可以缩小,不可以放大)(读写的可以只读,但是只读的不能读写)
const成员函数内 不可以调用 非const成员函数
非const成员函数 可以调用 const成员函数
初始化列表
构造函数体中的语句只能称作是赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值
初始化列表
1. 每个成员变量在初始化列表中只能出现一次(因为初始化只能初始化一次)
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量(引用的初始化要有一个实体,不能为随机或者空)
const成员变量(要用常量或者const变量,不能为随机或者空)
类类型成员(该类没有默认构造函数)
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关(在构造函数内部的赋值顺序和成员变量的声明无关)
static 成员
静态的成员变量在类内声明,要在类外初始化。
1. 静态成员为所有类对象所共享,不属于某个具体的实例
2. 静态成员变量必须在类外定义,定义时不添加static关键字
3. 类静态成员即可用类名::静态成员或者对象.静态成员来访问
4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员(因为它不属于某个具体的实例)
5. 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值
- 静态成员函数不可以调用非静态成员函数(因为非静态成员函数的第一个参数是this指针,而静态成员函数没有this)
- 非静态成员函数可以调用类的静态成员函数
友元
友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
- 友元函数可访问类的私有成员,但不是类的成员函数
- 友元函数不能用const修饰(const是为了修饰this指针的,而this指针是成员函数才有的,友元函数其实是普通函数,不属于成员函数)
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制(也就是在private 和 public 下都一样)
- 一个函数可以是多个类的友元函数
- 友元函数的调用与普通函数的调用和原理相同
友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
- 友元关系是单向的,不具有交换性。 (如:在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。)
- 友元关系不能传递 (如:B是A的友元,C是B的友元,则不能说明C时A的友元。)
内部类
如果一个类定义在另一个类的内部,这个内部类就叫做内部类。注意此时这个内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类。外部类对内部类没有任何优越的访问权限。
(注意:内部类就是外部类的友元类。注意友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。)
- 内部类可以定义在外部类的public、protected、private都是可以的。
- 注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。
- sizeof(外部类) = 外部类,和内部类没有任何关系。