C++PrimePlus-类与类继承

github:https://github.com/AnkangH/CSDN/tree/master/%E7%B1%BB%E7%BB%A7%E6%89%BF

第10章 对象和类

oop特征 抽象 封装和数据隐藏 多态 继承 代码可重用性

 将抽象转化为用户定义的类型 包括数据(成员)和操作数据的方法(成员函数)

共有私有 public和private 使用类对象的程序都可以直接访问类的共有部分,但是只能通过共有成员函数(或友元函数)来访问对象的私有成员。private可以省略,因为类对象默认私有

成员函数定义 定义是需要使用作用域解析符::来标示函数所属的类,定义时,可以访问类的私有成员

调用成员函数 调用成员函数被称为发送消息,因此当两个对象调用同一个成员函数时,执行同样的方法,但是作用于不同的数据。

类声明 在头文件中声明类的私有成员和共有成员,在源文件中定义成员函数,要注意成员函数的形参要与类的私有成员不同名。如果未提供类构造函数,则声明类的对象时不可初始化。类的声明只是描述了对象的形式,没有创建对象,因此不会分配储存空间。所以在类中初始化是不可行的。如果想创建所有类对象共享的常量,可以使用枚举或是static关键字,在类声明中使用static声明的常量,储存在静态存储空间,而不是类的对象中。

C++PrimePlus-类与类继承_第1张图片

构造函数 在声明类对象时对其私有成员初始化的函数。构造函数与类名相同。构造函数的参数为要传递给类私有成员的值而不是类的私有成员。构造函数还可用于对象赋值,如stock st=stock(“Mark”,100.1);首先创建一个=号右侧值的stock对象,将其值赋值给st后,该对象被释放或是将这句解释为stock st{“Mark”,100.1}。

析构函数 用来追踪对象的存在。特别是当类定义中使用了new创建变量,那么在析构函数中应该释放这些内存。
成员函数的定义不可放到main文件中,否则会造成名称冲突。一个头文件进行类的声明,一个源文件定义类的成员函数,另一个源文件做调用。最好不要使用using namespace std而是使用作用域符::

C++PrimePlus-类与类继承_第2张图片C++PrimePlus-类与类继承_第3张图片C++PrimePlus-类与类继承_第4张图片

C++PrimePlus-类与类继承_第5张图片
st1和st2使用了自定义构造函数,st3使用默认构造函数,当程序执行完毕后,3个对象都被释放。

this指针 当类成员函数涉及两个对象时,为了区分私有成员的归属,使用this指针。在定义成员函数时,参数为传递给它的对象,而调用成员函数的对象,使用this指针指代。注意this是指向对象的指针,因此访问对象的成员使用间接访问this->,获取对象使用解除引用*this

 

对象数组 定义类后,类作为与基本类型相同的类型,因此可以定义对象数组。数组的每个元素为一个类对象。对对象数组进行的操作同对基本类型数组进行的操作。

类作用域 在类中定义的名称,作用域为整个类。反之,作用域为类的名称只在类中是可知的,在类外不可知。这意味着必须通过类的对象调用类的共有成员。

第11章 使用类

运算符重载 运算符重载是c++多态的一种形式,针对不同的类型执行不同的操作,如同样是’+’算术加法运算符,int+int执行整形加法 double+double执行浮点型加法,string+string执行字符串拼接。运算符重载后,既可以通过非成员函数重载,也可以使用成员函数版本。=不需重载,任何类的对象,=都是赋值,所有成员均对应赋值。=即便重载也只能使用成员函数版本。



C++PrimePlus-类与类继承_第6张图片

创建st1时使用构造函数

创建st2时使用构造函数

+运算符重载函数中的temp析构

st1+st2的和先赋值给一个临时变量temp2然后再赋值给st3,temp2析构

显示st3的名称和数值

st1,st2,st3析构

运算符重载的限制

不能重载标准类型的运算符,如重载int+int

不能修改运算符的原性质,如操作数,优先级等

不能创建新的运算符 不能重载某些规定运算符 如成员运算符.作用域运算符::sizeof运算符

= () [] ->重载后只能通过成员函数重载

友元 只有类的成员函数才可以访问类的私有成员,因此提供了友元,使得友元与类的成员函数具有相同的访问权限。友元有三种 友元函数,友元类,友元成员函数。友元函数只有声明中使用friend关键字,定义中不使用friend关键字,且作用域不是类。


C++PrimePlus-类与类继承_第7张图片C++PrimePlus-类与类继承_第8张图片
 

这样st1*3时与第一个运算符重载匹配,3*st1与友元函数匹配。在程序中无论数字在前还是在后均能正确运行。

重载后的运算符 左侧为调用重载运算符函数的对象。如果重载了*号运算符,使其将对象的某个数字扩大n倍。那么a=b*n;是允许的,而a=n*b是不允许的,因为n是整形而不是类对象。解决问题是要么遵守类对象在前的规则,要么使用成员函数重载版本。解决方法是,像定义常规函数那样,将两个值均作为函数的参数。那么这个常规函数必须能够访问类的私有成员。使得+,*可以对类对象和整形或浮点型运算执行正确操作。

C++PrimePlus-类与类继承_第9张图片

C++PrimePlus-类与类继承_第10张图片C++PrimePlus-类与类继承_第11张图片
假设运算符需要k个操作数,那么成员函数版本的需要k-1个参数,因为有一个隐式的通过this指针传递,而友元函数则需要k个操作数。

第12章 类和动态内存分配

类的静态存储 在类中使用static声明的成员被所有类共享,如记录所有类对象的个数,在构造函数中+1,在析构函数中-1。 其存储在静态存储内存中,而不是类对象中。在类声明中只能声明该变量,但是不能初始化,否则头文件被重复包含后违反单定义原则。需要在类的源代码中定义该变量的初始值。如在stock.h中声明该变量,在stock.cpp中初始化该变量并定义成员函数对其执行的操作。在main.cpp中对类对象进行操作。

C++PrimePlus-类与类继承_第12张图片C++PrimePlus-类与类继承_第13张图片

特殊成员函数 如果不提供函数定义,那么C++自动提供以下函数定义

默认构造函数 默认析构函数 复制构造函数 赋值运算符 地址运算符

默认构造函数 stock::stock(){}不接受任何参数不进行任何操作 如果使用默认构造函数 那么累对象在声明后其值是不可知的。自定义后执行自定义初始化语句

复制构造函数与赋值运算符stock(const stock&);

何时被调用:新建将一个对象将其初始化为已有同类对象时,复制构造函数被调用,如显示初始化为已有对象。按值传递时会调用复制构造函数,函数返回对象时会调用复制构造函数。因此按值传递时应使用const stock& 代替stock。

执行操作:默认复制构造函数逐个复制非静态成员(浅复制),复制的是成员的值。如成员中有指针时,那么浅复制仅复制指针值,而深度复制复制指针指向的值。当处理字符串(char*)时,由于字符串复制是复制字符串地址,所以可能导致释放已经释放的内存两次。

c++11中使用关键字nullptr来代表空指针。对于赋值运算符:检测赋值情况,释放成员指针以前指向的内存,复制数据而不仅仅是数据的地址,返回指向调用对象的引用。

默认析构函数 

何时被调用:如果对象时自动变量,当执行完该对象的代码块时,析构函数被调用;如果对象是静态变量,程序执行结束时被调用;如果是动态变量(new创建的),那么在显式使用delete时,被调用。

第13章 类继承

类继承 从已有的类派生出新的类,而派生类继承了原有类(基类)的所有特征,包括方法。在已有类上添加功能,给类添加数据,修改类的方法行为。

公有继承(公有派生) 基类的共有成员称为派生类的共有成员,基类的私有成员也成为派生类的一部分,但是只能通过基类的共有和保护方法访问。派生类对象储存了基类的数据成员(继承了基类的实现),派生类可以使用基类的方法(继承了基类的接口)。反之基类不可使用派生类的成员和方法。派生类成员的初始化也可以使用成员初始化,与基类初始化放在一起。
C++PrimePlus-类与类继承_第14张图片C++PrimePlus-类与类继承_第15张图片
C++PrimePlus-类与类继承_第16张图片C++PrimePlus-类与类继承_第17张图片

派生类创建与释放 首先调用基类的构造函数,初始化继承的数据成员,然后再调用派生类的构造函数,初始化新增的数据成员。派生类的构造函数总是要调用一个基类的构造函数,释放的顺序相反,先调用派生类的析构函数,再调用基类的析构函数。

is-a关系 公有继承的派生类和基类通常是is-a关系,即基类包含派生类,派生类是基类的一种,is-a-kind-of简称is-a关系。由于继承,基类的引用或指针可以指向派生类,但是派生类的引用或指针不可以指向基类。假设有基类水果,派生类苹果,因为苹果是水果,所以指向水果的指针可以是苹果,但是指向苹果的指针不一定是水果,is-a关系可以很好的解释这种异同。当试图使用水果来给苹果赋值时,会发生问题。或者,另一种理解方式,基类的引用调用基类的方法,因为派生类继承了基类的方法,所以满足,但是派生类中可能有增加的方法,若能给派生类引用赋值基类,那么通过这个派生类调用派生类的方法会出现问题,因为派生类引用指向的对象根本就没有这个方法。同时,基类的指针和引用只能用于调用基类的方法,不可以使用基类的指针来调用派生类方法。基类的引用pt1可以赋值派生类对象,派生类引用pe1不可使用基类对象初始化。且pt1只有基类的方法,即便他现在指向了派生类对象。
C++PrimePlus-类与类继承_第18张图片

多态公有继承 希望同一个方法在基类和派生类中是不同的,为派生类修改基类中的方法。当基类调用该方法和派生类调用基类中的该方法时,执行不同的操作。叫做多态公有继承。为此,要在派生类中重新定义基类中的方法,或使用虚方法。

虚方法 在函数原形中使用关键字virtual,定义中不用关键字。使用虚方法后,程序将根据引用或指针指向的对象类型来选择方法,否则将根据引用或指针的类型来选择。因为基类的引用或指针也可以指向派生类。
C++PrimePlus-类与类继承_第19张图片C++PrimePlus-类与类继承_第20张图片

基类stock和派生类person中都声明了show()方法,且不是虚方法。pt1是基类引用,指向派生类。pe1是派生类引用,指向派生类。当不使用虚方法时,按照引用或指针的类型选择方法。所以pt1执行了基类的方法(不使用虚方法时,基类指针或引用只能执行基类的方法,),pe1执行了派生类的方法。

在函数原形中使用了virtual关键字后,引用或指针将根据指向的对象类型来选择方法。

虚析构函数 析构函数根据指针或引用类型调用对应的析构函数,所以当基类引用或指针指向派生类时,调用的是基类的析构函数,因此指向的派生类仍然存在。如果想在析构函数中执行操作,那么应该将析构函数声明为虚函数。当在基类中声明为虚析构函数函数时,如果基类引用指向派生类,那么会先调用派生类的析构函数,再调用基类的析构函数。

联编(函数名联编) 将函数调用解释为执行特定函数代码块。

静态联编 在编译过程中进行的联编,或称早期联编。

动态联编 在程序运行时选择正确虚函数代码

向上强制转换 将派生类基类引用或指针转换为基类引用或指针。向上转换是可传递的,如stock派生出stockChina stockChina派生出stockChinaSH,那么stock引用或指针可以指向stock对象,stockChina对象或stockChinaSH对象,即stockChina引用或指针可转换为stock指针,stockChinaSH引用或指针可以转换为stock指针。

向下强制类型转换 与向上强制转换相反。如果不使用显式强制类型转换,则向下强制类型转换不允许。上文中的派生类指针指向基类对象不允许,即基类的指针或引用不可以指向派生类的指针或引用。

虚函数注意点 构造函数不可以是虚函数。创建派生类对象时,先调用派生类的构造函数,在派生类的构造函数中调用基类的构造函数。派生类不继承基类的构造函数。所以将构造函数声明为虚函数没有意义。

析构函数 析构函数应当是虚函数,除非类不用做基类。即基类的析构函数必须为虚函数。

基类的析构函数未声明为虚函数。基类的指针指向了派生类,当delete该指针时,只调用了基类的析构函数。派生类对象未被释放。

当基类的析构函数声明为虚函数。当基类指针指向派生类时,delete该指针,会调用派生类的析构函数,然后再调用基类的析构函数。两个对象都被释放了。

你可能感兴趣的:(C++,c++,类,类继承,c++,primer,plus)