首页 > 代码库 > C++笔记(仅C++特性,需C语言基础)

C++笔记(仅C++特性,需C语言基础)

C++复习笔记一(类的声明定义应用与构造函数析构函数部分)
const在C语言中是"不能被改变值的变量",而在C++种子则是"一种有类型描述的常量",常量必须初始化,并且不可以改变
const int *P=15;const 在*的左边,所以*p是常量,不可以改变
int * const p=15;const在*的右边,所以p是常量,但是*p不是,*p是int型,*p的值可以改变
const型的引用只能用const型,不能用普通类型来引用,因为普通类型引用可能会修改const类型的值
正确的:const int A=5;const int ;&B=A; 
错误的:const int A=5;int &C=A
const类型引用可以引用普通数据,但是不能通过引用改普通数据的值;
const类型引用与被引用类型不同的时候会在初始化的时产生一个临时变量,强制转换后给const引用
普通类型的引用与被引用的类型不同时,是不能被通过的.
引用访问是一个变量的直接访问,而指针里面需要保存变量地址,所以是间接引用
引用是一个变量的别名,本身不单独分配自己的内存空间,它不是一个单独的变量,而指针有自己的内存空间,
引用一经初始化不能再引用其他变量,而指针可以(非const指针)


三种传参方式的比较
1.值传递:实参要初始化,形参要分配空间,将实参的内容拷贝到形参
2.指针传递:本质仍然是值传递
3.引用传递:实参初始化形参的时候不分配空间


C++是面对对象思想的原因,具有封装,继承和多态三大特征
封装是基础,继承是关键,多态是补充


类定义的一般形式:
class<类名>
{
public:(公有的)
protected:(保护的)
priovate:(私有的)
};


对象的定义<类名><对象名>


类与结构体的区别
1.默认访问权限,当没有特意声明访问权限的情况下,结构体的访问权限为public(公有的),而类是private(私有的)
2.初始化的区别,类可以通过构造函数和析构函数类初始化数据成员,而结构体只能使用特定于法来初始化


在构造函数中初始化数据成员,每当建立对象的时候,将自动调用构造函数来初始化数据成员
构造函数无返回类型,不能是虚函数
在定义是,若类的数据成员是另一个类的对象,则在调用构造函数创建对象时,对作为数据成员的对象先要自动调用其自身的构造函数


析构函数的额特点:
1.无返回类型
2.无参数
3不能随意调用(可以调用)
4.不能重载(构造函数有参数也可以重载)
5.析构函数和构造函数的功能相对应,所以在前面加一个逻辑反运算符"~"


下面这些情况需要用到析构函数:
1.当对象结束其生命周期的时候,比如在函数体内定义的对象,当函数调用结束时,局部对象被释放
2.构造函数打开一个文件,使用完文件时,需要关闭文件
3.从堆里面分配了动态内存区,在对象消失之前必须释放


构造函数先调用别人的,析构函数先调用自己的


const成员的初始化只能在构造函数初始化列表中进行,引用成员的初始化只能在构造函数初始化列表中进行,有参构造的对象成员(对象所对应的类没有默认构造函数)的初始化,也只能在构造函数初始化列表中进行


转换构造函数的作用是将一个其他类型的数据转换成一个类的对象,当一个构造函数只有一个参数,而该参数又不是本类的const引用时,这种构造函数成为转换构造函数
当单个参数的构造函数之前加上explicit关键字,就会阻止转换构造,同时也阻止了在定义对象是使用等号初始化对象
例子:
class TT
{
public:
TT(int data){m_data=http://www.mamicode.com/data;}
private:
int m_data;
};
TT obj = 10;//无法从int转换到TT;


构造函数可以被重载,所以C++根据类体重声明构造函数的参数类型和个数选择合适的构造函数


C++规定,每一个类都必须有一个构造函数,没有构造函数就不能创建任何对象,若未定义一个类的构造函数,则C++提供了一个缺省的构造函数,该构造函数是一个无参数的构造函数,仅仅负责创建对象,而不做任何初始化工作
只要类定义了一个构造函数,C++就不再提供缺省的构造函数,如果还需要无参数的构造函数,则需要自己定义,与定义变量类似,在用缺省构造函数创建对象时,如果创建的是全局对象或静态对象,则对象成员数据全部为0,局部对象创建时,其成员数据是无意义的随机数
一个类如果什么都没有则被称之为空类,一个空类的大小为一个字节,且编译器会为其隐式产生6个成员:
1.默认构造函数
2.默认拷贝构造
3.默认析构函数
4.默认赋值运算符重载
5.默认取址运算符重载
6.取址运算符const


C++提供了一个用一个对象值创建并初始化另一个对象的方法,完成该功能的就拷贝构造函数
拷贝构造函数的格式如下
<类名>::<拷贝构造函数名>(<类名>&<引用名>)
TT::TT(TT&obj)
如果一个类中没有定义拷贝构造函数,则系统自动生成一个缺省拷贝构造函数,其功能是将已知对象的所有数据成员的值拷贝给对应对象的数据成员
拷贝构造函数的特点:
1.拷贝构造函数与类同名,没有返回类型
2.拷贝构造函数只有一个性参数,该参数是该类对象的引用
拷贝构造函数除了用于使用已知对象的值创建一个同类的新对象外,还有两个主要用处:
1.把对象作为实参数进行函数调用时,系统自动调用拷贝构造函数实现把对象值传递给形参对象
2.当函数的返回值为对象时,系统自动调用拷贝构造函数对返回的对象值创建一个临时对象,然后再将这个临时对象赋值给接受函数返回值的对象

总结:
构造函数是一种用于创建对象的特殊成员函数,调用它为类对象分配空间,给他的数据成员赋初值,以及其他请求资源的工作,析构函数是一种用于撤销对象,回收对象所占有资源的特殊成员函数,它与构造函数功能互补,成对出现
每个类对象都必须在构造函数中诞生,一个类可能定义一个或者多个构造函数,编译程序按照对象构造函数声明中的形参数与创建对象的实参数进行比较,来确定使用哪个构造函数,这与普通的重载函数使用方法类型,在包含有对象成员的类对象被创建时,需要对对象成员进行创建,相应的要调用对象成员的构造函数
拷贝构造函数用于由一个已知的对象创建一个新对象
缺省构造函数和缺省析构函数用于在类中未显式定义构造函数和析构函数的情况,以创建一个对象(值分配数据成员的存数空间,不能初始化值),自动调用缺省的析构函数,以撤销一个对象并回收资源


C++复习笔记二(静态成员与友元函数)
全局对象是实现数据共享的一种方法,由于它处处可见,因此不够安全,应尽量在程序中少用全局对象,实现类的多个对象之间的数据共享,可以使用静态成员

静态成员包括静态数据成员和静态成员函数,友元函数也是一种普通的C++函数,但是它可以访问类的保护或私有成员,方便编程,提高了效率同时也破坏了类的封装性

在一个类中,若将一个数据说明为static.则该数据称为静态数据,它告诉编译器无论建立多少个该类的对象名都只有一个静态数据的拷贝,这个拷贝被所有类对象共享,静态数据属于类而共享,不属于对象独有,它的值对每个对象都是一样的,对静态数据成员的值得更新,即是对所有对象的该静态数据成员值得更新,口没后台数据使用关键字static,静态数据成员在类体中说明,在类体外定义,以分配空间并初始化.

静态数据成员可应用在以下几个方面:
1.用来保存流动变化的对象个数
2.作为一个标志,指示一个特定的动作是否发生
3.指向链表第一成员或最后一个成员的指针

静态成员函数
对静态成员函数的访问可以使用以下方式:
<类名>.<静态成员函数名>(<参数表>)
ClassA::Fun(123,456);

<对象名>.<静态成员函数名>(<参数表>)
objA.Fun(123,456)

静态成员函数只属于一个类,不属于类中的任何对象
静态函数的说明和定义与静态数据一样,函数实现可以在类体内也可以在类体外,与一般成员函数相同
在静态成员函数的实现中,可以直接使用静态成员,可以通过类对象来使用非静态成员

友元:
类具有封装性,类中的私有数据只有通过该类的成员函数才可以访问,如果在程序中需要访问类的私有成员,就必须通过对象来调用类的成员函数,频繁调用成员函数将影响程序的运行效率
为了解决以上问题给,c++提供了一种友元机智,友元可以不通过调用成员函数就可以直接访问类的私有数据,以提高程序的运行效率,友元机制在数据封装这堵不透明的墙上面开了一个小孔,因此使用友元要慎重,友元可以是一个函数,称为友元函数,友元也可以是一个类,称为友元类

当生命一个类A为另一个类B的友元时,友元类A中的所有成员函数都是类B的友元函数 

总结:使用静态数据成员比使用全局变量更优越,全局变量给面向对象程序设计带来的问题就是违背了数据封装的原则,要使用静态成员数据必须在main()程序运行之前分配空间和初始化,静态成员不与类的任何特定对象相关联,友元的作用主要是为了提高程序的运行效率和方便编程,但是随着硬件性能的提高,有缘的作用也不再明显,相反,友元 破坏了类的封装性,所以使用时需要权衡利弊.


C++复习笔记三(运算符重载)
除了以下四种运算符之外,其他运算符都可以被重载
1.条件运算符?:
2.分量运算符.
3.范围解析运算符::
4.取大小运算符sizeof

运算符重载要保持原运算符的以下特性不变:
1.优先级和结合性不变
2.操作个数不变
3.语法结构不变

运算符重载实际上是通过定义一个函数来实现的
     运算符重载归根结底是函数的重载,编译器选择重载的运算符是遵循函数重载的选择原则,即按照不同的类型或个数的参数来选择不同的重载运算符
运算符应该符合使用习惯,便于理解
     如果在字符串类中把"*"运算符重载为两个对象的合并操作,就不如把"+" 运算符重载,更便于理解
运算符重载不能创造新的运算符号
     例如不能创造一个"**"来计算幂
在C++中,运算符重载是通过运算符重载函数来实现的,运算符重载函数一般采用下述两种形式之一:
1.成员函数的形式
2.友元函数的的形式

成员函数形式的运算符重载
     <函数类型><类名>::operator<符号>(参数表){运算符重载函数体}
     int ClassA::opreator+(int A,int B){}

参数表列出该运算符需要的操作数,单目运算参数表中无参数,调用该函数的对象为操作数,双目运算参数表中有一个参数,调用该函数的对象为第一操作数,参数表中的参数为第二操作数,运算符函数体对重载的运算符的含义作出了新的解释,这种解释仅仅局限在重载该运算符的类中,运算符含义有函数体解释;否则脱离类对象,该运算符具有系统预定义的含义.

总结:
使用运算符重载可以使程序更易于理解并易于对对象进行操作,但是应该注意运算符重载不能改变运算符本身的优先级和结合性,不能改变操作数的数量,也不能发明新的运算符,如果在类中未定义拷贝构造函数和赋值运算符,编译器将提供缺省拷贝构造函数和赋值运算符,但是只能对简单的类对象适用
运算符重载函数可以定义为成员函数和友元函数两种形式,对运算符重载函数的调用采用显示和隐式方式进行,在前增量和后增量预算内酸服定义中,使用形参数int,只是为了标志前后有别,没有其他作用,拷贝构造函数用已经存在的对象创建一个相同类的新对象,而赋值运算符把一个对象的成员变量赋予一个已经存在的同类对象的同名成员变量中

C++复习笔记四(派生和继承)
  在C++中继承分为单继承和多继承
单继承:派生类只有一个直接基类的继承方式
多继承:派生类有多个直接基类的继承方式

派生类的定义格式
class <派生类名>:<继承方式><基类名>
{
//派生类中新成员的定义
}
继承方式分为三种:
public(公有继承)
基类中的每个成员在派生类中保持同样的访问权限
private(私有继承)
基类中的每个成员在派生类中都是私有成员,而且不能再被派生的子类所继承
protected(保护继承)
基类中公有的和保护都成员在派生类中都是保护的成员,私有的成员在派生类中仍为私有的
不管是什么继承方式,派生类中的成员函数和友元函数都可以访问基类中的公有成员和保护成员,但不能访问私有成员
在公有继承时,派生类的对象只能访问公有成员,在保护继承和私有集成式,派生类的对象不能访问基类中的任何成员

多继承有多个基类,基类名之间用逗号分割,每个基类名前都应该有一个该基类的继承方式说明
class <派生类名>:<继承方式><基类名>,<继承方式><基类名2>
{
//派生类中新成员的定义
}
缺省的继承方式为私有继承

可以使用<基类>::<成员名>的声明方式在派生类中将基类中的公有成员从私有继承的派生类中声明为公有的,使得派生类的子类对象可以访问成员

派生类中的构造函数/析构函数
通过继承关系,派生类包含了它所有的基类成员,派生类对象的数据结构由积累中说明的数据成员和派生类中说明的数据成员共同构成,在创建派生类对象时,派生类对象的初始化,不仅要给派生类中的数据成员舒适化,还要给它基类中的数据成员初始化,如果派生类中还有子对象时,还应包含对子对象初始化
<派生类名>(<总参数表>):<基类构造函数名>(参数表1),(子对象名)(参数表2)
{
\\派生类中数据成员的初始化
}
构造函数的调用顺序如下
1.基类构造函数
2.派生类构造函数
执行派生类的析构函数时,也要调用基类和子对象的析构函数
析构函数的顺序如下
1.先调用派生类的析构函数
2.基类的析构函数

类的成员重定义
重定义是子类需要修改或扩展积累的某个成员功能时需要利用的机制
重定义分别可以对基类的数据成员的重定义,或对基类成员函数的重定义
重定义的新成员既可以与基类完全相同,也可以与与基类函数名相同而参数不同

注意:
1.不管子类重载的成员函数的参数与基类是否完全相同,都会构成重载
2.重定义是指在不同的作用域中定义的成员函数名相同,参数不同或相同的情况
3.重定义后的成员会覆盖掉其父类成员

多继承派生类的构造函数格式
<派生类名>(总参数表):<基类名>,(参数表),<基类名2>,(参数表2),<子对象名>,(参数表)
{
//派生类构造函数
}
TTC(int a):TTB(j),TTA(j),m_nNum(15)
{
//派生类构造函数
}
执行顺序:限制性所有基类的构造函数,,再执行派生类本身的构造函数,包含子对象在内

多继承中的二义性问题
一般来说,派生类中的成员访问都是唯一的,但是在多继承的情况下,可能出现派生类对其类成员访问的不唯一性,即二义性,下面是出现二义性的两种情况:
1.调用不同类的具有同样名字的成员时可能出现二义性
2.访问共同基类的成员时可能出现二义性
解决办法是用类名加以限定:<派生类对象名>.基类名::成员名;

虚基类说明格式如下:
class <基类名>
class <派生类名>: virtual <继承方式><基类名>
class <派生类名>:virtual public CClassA

引进虚基类的目的是为了解决二义性的问题,使用公共积累在其派生类对象中产生一个基类子对象
如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员,c++提供的虚基类的方法,使得在继承间接共同基类时只保留一份成员

总结:
C++支持类继承机智,继承是面向对象的重要概念之一,派生类的成员函数和友元函数可以访问积累的所有公有和保护的数据成员和成员函数
派生类对象只能访问基类的公有数据成员和成员函数,多重继承是一个类从多个基类派生而来的机制,派生类实际上获取了所有基类的特性
当一个类是两个或多个基类的派生类时,必须在派生类名和冒号之后,列出所有基类的类名,基类之间用逗号隔开,派生类的构造函数必须激活所有基类的构造函数,并把相应的参数传递给它们
派生类可以是另一个的基类,这样就相当于形成了一个继承链,当派生类的构造函数被激活时,他的所有基类的构造函数也会被激活,为解决多继承中的二义性问题,引进了虚基类的概念,七亩地是使公共基类在其派生对象中指残生一个基类子对象


C++复习笔记五(多态与虚函数)
虚函数是一种非静态的成员函数,定义格式如下:
virtual <类型说明符><函数名>(参数表)
{
//函数体
}
virtual void fun_s()
{
//函数体          
}
其中virtual是关键字
如果某个类中的一个成员函数被说明为虚函数,该成员函数可能被派生类中存在着不同的实现版本
由于存在有虚函数,编译器将进行动态联编,使调用虚函数的对象在运行时确定,以实现动态联编的多态性
基类函数具有虚特性的条件是:
1.在基类中,将该函数说明为虚函数(virtual)
2.定义基类的公有派生类
3.在基类的公有派生类中重载该函数
4.定义指向基类的指针变量,它指向基类的派生类的对象

重载虚函数不是一般的重载函数,它要求函数名.返回类型,参数个数,参数类型和顺序完全相同
由于对虚函数进行重载,因此在派生类中的虚函数钱的virtual关键字可以省略

重载:同一种函数的不同实现[作用域相同 ,函数名相同,参数名不同]
重定义:子类覆盖基类的同名函数[作用域不同,函数名相同,参数可以相同也可以不同]
重写:子类覆盖基类的同名函数,函数类型相同[作用域不同,函数名相同,参数相同]

纯虚函数与抽象类
纯虚函数是一种特殊的虚函数,是一种没有具体实现的虚函数,其定义格式如下:
class<类名>
{
     virtual <函数类型><函数名>(参数表)=0;
}
class TT
{
virtual void XXXX()=0;
}
在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给了该基类的派生类去做,这就是纯虚函数的作用
存虚函数相遇接口模板,不能直接实例化,需要派生类来实现函数定义
体现面对对象设计的特点增删容易,修改方便,便于共享
一个类中如果定义了纯虚函数,则这个类就会变成抽象类,C++规定抽象刘类型将不能再定义对象

虚析构函数
构造函数不能说明为虚函数,而析构函数可以说明为虚函数,其方法是在析构函数前加上关键字virtual说明符,如果一个基类的析构函数被说明为虚函数,则其派生类中的析构函数也是虚席狗函数,则省略virtual说明符
虚析构函数可以采用动态联编,可在运行时选择析构函数,能确保析构函数说明为虚析构函数的正确执行,因此,在继承的情况下,将基类中的析构函数说明为虚析构函数是没有坏处的,
需要注意的是,虚析构函数一般用在基类,泳衣防止对衍生类对象delete基类指针造成的内存泄露,基类的析构函数为虚函数,且衍生了牛有自定义析构函数实现时,delete基类指针时会同事调用衍生类的析构函数,如果基类析构函数不是虚函数,那么久调用基类的析构函数,而基类的析构函数不可能释放衍生类的其他资源

C++笔记(仅C++特性,需C语言基础)