十三、类和对象总览
-
基础框架:
-
类 class 类名
-
包括:
-
1.访问权限
class默认权限为private 而struct默认权限为public
-
公共权限 public:
类内可以访问 类外不可以访问
-
保护权限 protected:
类内可以访问 类外不可以访问 子类可以访问
-
隐私权限 private:
类内可以访问 类外不可以访问 子类不可以访问
-
-
成员
-
2.属性(成员属性、成员变量)
-
3.行为(成员函数、成员方法)(多用函数定义)
-
创建一个类,C++默认创建:
-
1.默认无参构造函数(空实现)
-
2.析构函数(空实现)
-
3.拷贝构造函数(值拷贝)
也就是说拷贝构造函数默认就有,可以直接进行值拷贝
-
-
-
-
-
-
对象 定义:类名 对象名;
-
属性赋值 类名.对象名(不用写数据类型)
-
行为 对象名.行为名
-
-
-
class和struct的区别:C++ class和struct到底有什么区别详解
-
指向类的指针与->运算符
-
定义一个指向类的指针,然后可以让此指针指向类中的对象
Box Box1(3.3, 1.2, 1.5); // Declare box1 Box Box2(8.5, 6.0, 2.0); // Declare box2 Box *ptrBox; // Declare pointer to a class. // 保存第一个对象的地址 ptrBox = &Box1; // 现在尝试使用成员访问运算符来访问成员 cout << "Volume of Box1: " << ptrBox->Volume() << endl; // 保存第二个对象的地址 ptrBox = &Box2; // 现在尝试使用成员访问运算符来访问成员 cout << "Volume of Box2: " << ptrBox->Volume() << endl;
-
C++中箭头运算符->,相当于把解引用和成员访问符两个操作符结合在一起,换句话说,p->func()和(*p).func()所表示的意思一样。
-
-
构造函数和析构函数
-
构造函数
-
性质
-
1.创建对象时构造函数会自动调用且只调用一次
-
2.没有返回值,不用写void
-
3.函数名与类名相同 可以有参数,可以发生重载
-
-
分类
-
按参数:有参构造、无参构造
-
按照类型
-
普通构造
-
拷贝构造
-
定义格式:构造函数名( const 类名 &实例化个体 ){}
-
性质:
-
1.创建类时,构造函数默认添加,对属性进行值拷贝
-
2.若用户定义有参构造函数,C++不在提供无参构造,但是会提供默认拷贝构造
-
3.若用户定义拷贝构造函数,C++不再提供其他构造函数(包括默认构造函数)
-
-
什么情况下会调用拷贝函数:
-
1.普通的构造函数调用
-
2.值传递的方法给函数形参赋值
函数形参为对象
-
3.以值方式返回局部对象
以函数return的方式被返回
-
-
深拷贝与浅拷贝
-
定义
-
浅拷贝: 简单的赋值操作
浅拷贝就是简单的等号 默认的拷贝构造函数就是浅拷贝
-
默认拷贝构造里的代码段:
Person类: int m_age; int * m_height; Person(const Person &p) { m_age = p.m_age ; m_height = p.height ; }
-
解释:
p2拷贝p1,当p2析构函数调用后,p2.m_height在堆区的指向内存就会被释放,而p1.m_height也指向那个内存,当p1的析构函数调用时,因为p1.m_height所指向的那块内存已经被清除,所以程序会崩溃
-
-
-
深拷贝: 在堆区重新申请空间,进行拷贝操作
深拷贝是重新创建内存来进行拷贝
-
代码实现:
Person类: int m_age; int * m_height; Person(const Person &p) { m_age = p.m_age ; m_height = new int(m_height) ; }
-
p1.m_height与p2.m_height指向的不是同一个内存
-
-
-
-
如果属性有在堆区开辟的,一定要自己提供拷贝构造函数(深拷贝),防止浅拷贝带来的问题
-
-
-
-
-
调用:
-
1.括号法
-
Person p1; 默认构造函数
-
Person p1(10); 有参构造函数
-
Person p1(p2); 拷贝构造函数
-
-
2.显示法
-
Person p1; 默认构造函数
-
Person p1 = Person(10); 有参构造函数
-
Person p1 = Person(p2); 拷贝构造函数
-
-
3.隐式转换法
-
Person p1 = 10; 有参构造函数
-
Person p1 = p2; 拷贝构造函数
-
-
-
初始化列表
-
利用构造函数进行初始化列表
-
构造函数头部: Person(形参):属性1(值),属性2(值), ...
-
-
-
-
析构函数
-
性质
-
程序在对象被销毁前会自动调用析构,无须手动调用且只调用一次
-
没有返回值,不用写void
-
函数名是 ~类名
-
不能有参数,因此无法发生重载
-
-
-
-
子对象:类对象作为类成员(对象成员)
-
CSDN中的补充:A类有属性m_n,m_a即是A类的实例化对象,又是B类的属性。b为B类的实例化对象,则可这样调用属性:b.m_a.m_n
-
静态成员
-
静态成员变量
-
性质
-
1. 静态成员变量被类中所有的对象共享,共用同一份数据
-
2. 在编译阶段就分配内存,内存放在全局区
-
3. 类内声明,类外初始化
-
原因:
静态成员是单独存储的,并不是对象的组成部分。如果在类的内部进行定义,在建立多个对象时会多次声明和定义该变量的存储位置。在名字空间和作用域相同的情况下会导致重名的问题。
-
类外初始化的实质是类外定义:
C++的静态成员实际上需要分配一个内存,不一定需要赋值。初始化是赋一个初始值,而定义是分配内存。静态成员变量在类中仅仅是声明,没有定义,所以要在类的外面定义。 在定义或初始化时,全局区为此静态成员变量分配了一块内存。
-
-
4. 静态成员变量也有访问权限
-
-
访问方法
-
通过对象:
Person p1 ; cout<<p1.m_office ;
-
通过类:
cout<<Person::m_office ;
-
-
-
静态成员函数
-
性质
-
1. 静态成员函数被类中所有的对象共享
-
2. 只能访问静态成员变量
非静态属性属于单独的对象,函数没法区别到底是谁的
-
2. 静态成员函数也有访问权限
-
-
-
-
对象成员特性:
-
空类的对象 自动分配1字节空间
-
原因:
为了区分空对象占内存的位置 每个对象都有一个独一无二的内存地址,空对象也不例外 这1字节起到“小狗尿尿标记领地”的作用
-
-
非静态成员变量 属于类的对象
-
非静态成员函数 不属于类的对象,只有一份,所有对象共享
-
那么非静态成员函数如何知道是谁调用自己的呢?——this指针
-
-
所有静态成员 不属于类的对象,只有一份,所有对象共享
-
-
this指针:
-
性质
-
this指针指向被调用的成员函数所属的对象
谁调用的这个成员函数,this就指向谁
-
this指针隐含在每一个非静态成员函数中
-
this指针不需要定义,直接使用即可
-
this指针是一个指针常量,指针的指向不可被修改
-
-
作用:
-
1. 解决成员函数形参和成员属性重名的问题
-
2. 返回对象本身用*this
-
返回一个匿名对象,该对象属性值与本对象相同:
Person addage(Person p){ this->m_age += age ; return *this ; }
-
-
-
-
常函数和常变量
-
常函数
-
性质
-
本质上:在函数后面加const,修饰的是this指针,让指针指向的值(this->)也不能被改变
-
成员函数后加const后为常函数
-
常函数内不可以修改成员属性(只读)
-
成员属性在声明时加关键字mutable才可在常函数中被修改
-
-
-
常对象
-
性质
-
声明对象前加const为常对象
-
常对象中的数据成员为常变量且必须要有初始值
e,g: Person const p1(22,1)
-
常对象只能调用常函数
-
-
-
-
友元 friend
-
友元的目的是访问另一个类中的私有成员
-
三种实现
-
全局函数做友元
全局函数:在类外声明的函数
-
将函数声明前加friend放到类的定义的最前边
-
-
类做友元
-
将友元类的声明前加friend放到类的定义的最前边
-
友元类就可以访问此类的私有属性
-
-
-
成员函数做友元
-
将成员函数的声明前加friend放到类的定义的最前边,成员函数需要用 类名::来表明是哪一个类里面的成员函数
-
-
-
-
运算符重载
-
概念:
-
对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
-
-
分类:
-
加号运算符重载
-
作成员函数
-
Person operator+(const Person& p)
-
-
作全局函数
-
Person operator+(const Person& p1, const Person& p2)
-
-
链式编程思想:
-
返回值: 类
-
return 临时对象; //临时对象的成员为两对象成员的和
-
-
-
左移运算符重载
-
作用:自定义输出内容
-
成员函数 无法实现!
-
全局函数
-
ostream& operator<<(ostream& cout, Person& p) { out << "a:" << p.m_A << " b:" << p.m_B; return cout;}
-
-
链式编程思想:
-
返回值: ostream &
-
&的解释: ostream对象只能有一个
-
-
return: cout;
-
-
-
递增运算符重载
-
以++为例:
-
前置++:
先+1
-
返回值: 类&
-
return *this;
-
-
后置++:
后+1
-
先用临时对象temp存档: 类 temp=*this;
-
然后再将*this进行+1
-
最后返回temp
-
返回值: 类
-
注意:不能再加&,因为返回的是temp
不加&,则返回的是temp的副本,非temp
-
-
-
-
-
赋值运算符重载
-
编译器会自动在类内生成operator=函数,用来进行值拷贝
c++编译器至少给一个类添加4个函数 1.默认构造函数(无参,函数体为空) 2.默认析构函数(无参,函数体为空) 3.默认拷贝构造函数,对属性进行值拷贝 4.赋值运算符 operator=, 对属性进行值拷贝
-
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题
-
重载:
-
先判断成员堆区有无内存
-
if (m_Age != NULL) { delete m_Age; m_Age = NULL; }
-
-
进行深拷贝
-
m_Age = new int(*p.m_Age);
-
-
return *this;
-
返回值: 类& //链式编程思想
-
-
-
关系运算符重载
-
返回值:bool
-
return 0或1
-
-
-
-
面向对象的三大特性:封装继承多态
-
文件
-
类型:
-
1、文本文件 以ACLL码形式储存在计算机中
-
2、二进制文件 用户一般不能读懂
-
-
操作文件的三大类:
-
写操作 ofstream
-
读操作 ifstream
-
读写操作 fstream
-
-
文件打开方式:
-
ios::in 为读文件而打开文件
-
ios::out为写文件而打开文件
-
ios::ate 初始位置为文件尾
-
ios::app 以追加方式写文件
-
ios::turnc 如果文件存在先删除再创建
-
ios::binory 以二进制形式
-
多种打卡方式用 | 隔开
e.g. ios::in|ios::out
-
-
代码实现:
-
写文件:
-
加入头文件<fstream>
-
创建ofstream对象ofs1
-
打开文件:ofs1.open("路径+名字.文件类型" , 文件打开方式);
-
写文件:ofs1 << ... ;
-
关闭文件:ofs1.close();
-
-
-
-
-
-
-