StudyCC++.docx
- 文档编号:22899054
- 上传时间:2023-04-28
- 格式:DOCX
- 页数:63
- 大小:114.64KB
StudyCC++.docx
《StudyCC++.docx》由会员分享,可在线阅读,更多相关《StudyCC++.docx(63页珍藏版)》请在冰豆网上搜索。
StudyCC++
C/C++学习
目录
第一章类4
1.1类成员函数4
1.2解决类成员名和参数名的冲突4
1.3构造函数和析构函数4
1.3.1拷贝构造函数5
1.4静态成员6
1.4.1静态数据成员6
1.4.2静态成员函数6
1.5类对象作为数据成员7
第二章继承和派生8
2.1派生8
2.2派生类的构造函数和析构函数9
2.2.1派生类构造函数和析构函数的定义原则9
2.2.2虚析构函数:
10
2.3派生类对基类私有成员的访问11
2.4虚基类12
第三章多态性和虚函数15
3.1多态性15
3.1.1编译时的多态性——重载15
3.1.2运行时的多态性——虚函数15
第四章内存分配及其管理18
4.1存储18
4.1.1静态存储区(StaticMemory)18
4.1.2自动存储区(AutomaticMemory)18
4.1.3自由存储区(FreeStore)18
4.2new和delete的使用18
4.3free和delete的使用20
4.4重载new和delete20
4.4.1局部重载new和delete20
4.4.2全局重载new和delete21
第五章模板22
5.1函数模板22
5.2重载模板函数22
5.3类模板23
第六章异常处理25
第七章I/O及流27
7.1I/O流27
7.2文件I/O28
7.2.1读写文本文件28
7.2.1二进制文件操作29
7.3串流29
第八章其他31
8.1容器类31
8.2名字空间31
附录:
33
第一章类
结构和类既相似又有区别:
在C中,结构不能定义成员函数;而在C++中,结构可以定义成员函数。
在C++中,结构和类的不同点是:
缺省状态下,类成员是私有的,而结构成员是公有的;相同点在于:
两个结构变量或类对象可以相互赋值,也可以对结构变量或类对象逐个成员进行赋值。
1.1类成员函数
定义类成员函数的方法:
<1>内联函数定义。
内联函数的定义方式有两种,隐式定义(在类声明中实现函数,不使用inline关键词)和显式定义(在类声明或类外实现,实现时使用inline关键词)。
内联函数的基本思想在于将每个函数调用用它的代码体来替换。
一方面,如果内联函数体比较长,将增加类定义的规模和复杂性,很可能会增加整个目标代码的体积,成员函数的代码不能被该类所有的对象共享,系统要为每个对象创建一份拷贝,浪费了内存空间;另一方面,如果内联函数体非常短,编译器为这个函数体生成的代码就会真的比为函数调用生成的代码要小许多,内联这个函数将会确实带来更小的目标代码和更高的缓存命中率!
使用内联函数可缩短执行时间,但会增加程序长度,因此,inline关键词应只用于经常使用的小函数,编译器会忽略其他函数的inline关键词。
<2>外联函数定义(在类中声明成员函数,在类外实现成员函数,并且不使用inline关键词)。
C++编译器会为成员函数的指令创建唯一一份拷贝,供该类的任何对象共享,节省了内存空间。
1.2解决类成员名和参数名的冲突
在成员函数中,如果参数名和类成员变量一样时,参数名被使用,类成员名被隐藏。
通过在成员变量名前添加类名和双冒号,或使用this指针来解决。
VoidCLSA:
:
SetValue(intx,inty)
{
x=x;//setinvalid
CLSA:
:
y=y;//setvalid
This->y=y;//setvalid
}
在类成员函数中,可以使用this指针,也可以显式地传递this指针给另一个函数。
1.3构造函数和析构函数
在构造函数中可以使用缺省参数值,当调用没有为函数指定参数时,使用缺省变元。
缺省变元仅仅能放置在参数列的最后,也就是缺省变元后不能有其他参数。
classA{
A(intx,inty=0);//ok
//A(intx=0,inty,intz=0);//error
A(chara,intx=0,inty=0);//ok
}
voidmain()
{
Aa(3);//调用A(intx,inty=0),传递一个参数
Ab(6,9);//调用A(intx,inty=0),传递两个参数
Ac('a');//调用A(chara,intx=0,inty=0),仅传递一个参数
}
定义函数时,一个引用变量不能被定义为缺省参数。
初始化类成员的另一种方法如下,构造函数初始化类数据成员的顺序与数据成员在类中的声明顺序是一致的,与初始化类数据成员的排列顺序无关。
classCLSA
{
public:
CLSA();
intx,y;
}
CLSA:
:
CLSA():
x(4),y(x){
}
结果:
x、y都为4
classCLSA
{
public:
CLSA();
intx,y;
}
CLSA:
:
CLSA():
y(4),x(y){
}
结果:
y为4,x为系统分配数值
1.3.1拷贝构造函数
拷贝构造函数是一个特殊的构造函数,当创建一个新对象时,系统自动调用它;另外,它的功能在于将对象的参数列表逐域拷贝到新建的对象中。
拷贝构造函数的定义有两种形式:
<1>系统定义(缺省拷贝构造函数)
classpoint{
public:
point(intx,inty){
point:
:
x=x;
this->y=y;
}
private:
intx,y;
};
voidmain()
{
pointp1(3,4);
pointp2(p1);//缺省拷贝构造函数,将对象p1的各域逐个拷贝给对象p2
pointp3=p1;//调用系统或用户定义的拷贝构造函数,将对象p1的各域逐个拷贝给对象p3
}
<2>用户定义
拷贝构造函数定义形式如下左表:
point:
:
point(constpoint&p)
{
point:
:
x=p.x;
this->y=p.y;
}
point:
:
point(constpoint*p)
{
point:
:
x=(*p).x;
this->y=(*p).y;
}
拷贝构造函数的调用同上,这时程序调用用户定义的拷贝构造函数,而不是系统本身的拷贝构造函数。
但是,上表右侧函数不是用户自定义的拷贝构造函数。
在传递大型对象时,使用常量引用参数既能模拟传值调用的安全性,又能避免传递大型对象拷贝的开销。
1.4静态成员
类的静态成员可以是数据成员,也可以是成员函数。
静态成员的特征是,不管该类创建了多少个对象,其静态成员只有一个副本,这个副本被属于这个类的所有对象共享。
1.4.1静态数据成员
当创建类的第一个对象时,类的所有静态数据成员被初始化,若有没有被初始化的静态数据成员,系统报错;再次建立对象时,不需要对静态数据成员进行初始化。
类的所有对象共享类的静态数据成员,它不会随某个对象而改变。
静态数据成员可以由静态成员函数访问,也可以由非静态成员函数访问。
静态类对象,在声明时调用构造函数一次,程序终止时调用析构函数。
1.4.2静态成员函数
静态成员函数属于整个类,而不属于类中的某个对象。
静态成员函数仅能访问类中的静态数据成员或全局变量,不能访问非静态数据成员;静态成员函数仅能调用类中的静态成员函数和全局函数,不能掉用类中非静态成员函数。
静态成员函数中没有this指针,无法判断当前对象是哪一个,也无法判断静态数据成员属于哪个对象,所以如果在函数中需要确定对象,一般采用参数传递对象的方法。
在调用静态成员函数的前面,对象名和类名,必须缀上一个,否则是错误的。
在不强调调用对象的前提下,使用对象名和类名是一样的。
constchar*sys="Win32";//全局变量
classA{
public:
A(){
count++;};
staticintgetCount(void){
returncount;}
staticintgetCount(Aa){//传递对象
returna.count;}
staticchar*getStr(){
return(char*)sys;}
private:
staticintcount;
};
intA:
:
count=0;
voidmain()
{
Aa;
intnum=a.getCount();//前缀对象名a
num=A:
:
getCount();//前缀类名
num=A:
:
getCount(a);//前缀A:
:
charstr[20];
memset(str,0,20);
strcpy(str,a.getStr());
}
1.5类对象作为数据成员
某个类对象或指针可以成为另一个类的数据成员。
在对类调用构造函数进行初始化时,如果需要传递参数给对象成员,也必须调用内部对象所在类的构造函数进行初始化。
classA{
intcount;
public:
A(inti){
count=i;}
};
classB{
intnumber;Aa;//类对象为成员变量
public:
B(inti);
};
B:
:
B(inti):
a(i){//不能写成B:
:
B(inti):
A(i)或B:
:
B(inti):
a(intj)
number=i;}
voidmain()
{
Bb(100);
}
第二章继承和派生
2.1派生
派生类或派生类的使用者均不能访问基类的私有数据成员;对于基类的公有成员的访问,如果派生的方式不同,访问的权限也不同。
派生时,不指明派生类型的,按私有派生进行派生。
<1>私有派生
由私有派生得到的派生类,对基类的公有成员只能是私有继承,也就是基类的所有公有成员只能成为派生类的私有成员,这些私有成员只能被派生类的成员函数访问,派生类的使用者无权访问。
此时,基类中的保护段成员也成为派生类的私有成员。
如果希望基类中的某些公有成员、保护成员在私有派生类中也是公有、保护的,使得派生类和派生类的使用者可以访问它,则可以在派生类中的公有段或保护段中说明这些成员,并在成员名前缀上“类名:
:
”。
classA{
intcount;
public:
A(inti=0){
count=i;}
intgetCount(){
returncount;}
};
classB:
A{//私有派生
public:
A:
:
getCount;//声明基类的公有成员为派生类的公有成员
/*protected:
A:
:
getCount;*///声明基类的公有成员为派生类的保护成员
};
voidmain()
{
Bb;
intnum=b.getCount();
}
声明基类的公有成员为派生类的公有成员,只需要注明类名和函数名,不需要声明函数参数和返回值,因为该函数在基类中已经声明过。
私有段中不能进行声明。
派生类中有与基类相同的函数时,这种声明是无效的,派生对象调用的函数是派生类中的,而不是基类的。
在派生类公有段中声明基类的构造函数,如果基类构造函数(重载情况下)同在公有段中,则基类的构造函数成为派生类中的公有成员;如果基类构造函数处于不同的段中,系统将根据派生类构造函数调用基类构造函数的使用,决定这种声明的有效性。
ClassA{
protected:
intcount;
voidprint1();
public:
intnum;
voidprint();
A();
A(inti);
};
classB:
A{
protected:
A:
:
count;
A:
:
print1;
public:
B();
voidprint();
//A:
:
count;//error不能同时声明
A:
:
A;
A:
:
num;
A:
:
print;//声明无效
};
classA{
protected:
A(inti);
public:
A();
};
classB:
A{
public:
B():
A(){
}
A:
:
A;//public段A()有效
/*B():
A
(2){
}
A:
:
A;*///protected段A()有效
};
<2>公有派生
通过公有派生得到派生类,基类中的公有成员在派生类中仍然是公有成员,基类中的保护段成员在派生类中仍然是保护成员。
<3>保护派生
通过公有派生得到派生类,基类中的公有成员和保护段成员,在派生类中是保护成员。
2.2派生类的构造函数和析构函数
2.2.1派生类构造函数和析构函数的定义原则
在以下情况下必须定义派生类的构造函数
<1>派生类本身需要构造函数
<2>在定义派生类对象时,其相应的基类需要调用带参数的构造函数。
构造函数格式:
派生类构造函数(参数):
基类构造函数(参数),对象成员1构造函数(参数),。
。
。
对象成员n构造函数(参数){//基类和对象成员构造函数的排放顺序不分先后
}
如果基类使用缺省的构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去“:
基类构造函数(参数)”;如果派生类对象不需要初始化,也不必定义构造函数。
派生类的析构函数的定义与基类没有关系。
如果基类、成员类和派生类都有构造函数,执行顺序分别是先基类、再对象成员、后派生;析构函数时的执行顺序正好相反,先派生、再对象成员、后基类。
classA{
intcount;
public:
A(inti=0){
count=i;
}
~A(){
}
};
classB{
public:
B(char*str){
memset(s,0,20);
strcpy(s,str);
}
~B(){
}
private:
chars[20];
};
classC:
A{
public:
C(intnum);
~C(){
}
private:
intnumber;
Bb;
};
C:
:
C(intnum):
b("CHINA"),A(num){
number=num;
}
voidmain(void)
{
Cc(100);//先A(num),再b("CHINA"),最后C:
:
C(intnum)
}//析构时,先~C(),再~B(),最后~A()
2.2.2虚析构函数:
c++语言标准关于虚析构函数的阐述:
当通过基类的指针去删除派生类的对象,而基类又没有虚析构函数时,结果将是不可确定的。
实际运行时经常发生的是,派生类的析构函数永远不会被调用。
声明析构函数为虚就会调用基类和派生类的析构函数。
#include
classBase
{
public:
virtual~Base(){cout<<"~Base"< }; classDerived: publicBase { public: virtual~Derived(){cout<<"~Derived"< }; voidmain(void) { Base*pB=newDerived;//upcast deletepB;//对象内存释放时,Derived和Base的析构函数都会被调用。 } 输出结果为: ~Derived ~Base 如果析构函数不为虚,那么输出结果为 ~Base 2.3派生类对基类私有成员的访问 在公有派生和私有派生中,继承类都不能直接访问基类的私有成员。 继承类访问基类的私有成员的方法有以下两种方法: <1>在基类中增加保护类成员 将基类私有成员中需要提供给派生类访问的部分定义为保护段成员。 基类的保护类成员可以被派生类(无论公有派生、保护派生还是私有派生)访问,但外界对象不能访问。 classA{ protected: intcount; public: A(inti=0){ count=i; } voidprint(){ cout< } }; classB: publicA{ intnum; public: B(inti){num=i;} voidprint(){ cout< } }; classC: B{ public: C(): B(10){} voidprint(){ cout< //cout< } }; voidmain() { Bb(4); Cc; b.print(); c.print(); } <2>将需要访问基类私有成员的派生类成员函数声明为基类的友元 在基类中增加保护类成员的方法,存在一个弊端,多重公有继承或保护继承得到的派生类,仍然可以访问原基类的保护段成员。 可以通过将需要访问基类私有成员的派生类成员函数声明为基类的友元的方法,使得派生类中的指定成员函数函数可以访问基类的私有成员,其他的成员函数则不能访问基类的私有成员。 通过友员访问基类的私有成员,这种访问不是经常的,但是必要的。 否则,需要类提供足够的成员函数。 友元的使用使得C++中的数据封装性受到削弱,导致程序的维护性变差(慎用)。 声明为某个类的友元的外界对象既可以是另一个类的成员函数,也可以是不属于任何类的一个函数,还可以是某个类(此时,该类中的所有成员函数都成为友元)。 设置友元的首要目的是可以使友元函数访问(使用或改变)某类的私有成员。 友元的声明可以放在类的公有部分,也可以放在类的私有部分。 classcircle; classpoint{ intx,y; public: point(intx,inty); friendvoidprint(constpointp);//声明不属于任何类的函数为友元函数 friendclasscircle;//声明某类。 为友元类 friendvoidcircle: : getCircleCenter(int&x,int&y);//声明某类的函数为友元函数,该函数可以访问point的私有成员x和y } 说明: 友元函数虽然可以访问类对象的私有成员,但是它没有this指针,在某一时刻究竟是应该访问哪一个对象的私有成员,很难确定。 可以通过向友元函数传递对象的办法,显式地确定对象。 友元关系是“给予”的,而不是“索取”的。 也就是说,如果类B是类A的友元类,类A中必须声明类B是类A的友元类。 友元关系是不对称且不传递的。 2.4虚基类 在如下左图所示的多层继承类中,类P两次成为类C的间接基类,此时,C类对象中有两个类P的对象: 由A继承的P和由B继承的P,C类对象的内存排列如中图所示: 虚基类: 当在多条继承路径上有一个公共的基类,在这些路径中的某几条路径汇合处,这个公共基类会产生多个实例,如果需要只保存基类的一个实例,可以将这个公共基类声明为虚基类。 虚基类的声明如下表右侧所示: 非虚基类的多重继承 classP { public: intnext; }; classA: publicP { }; classB: publicP { }; classC: publicA,publicB { public: voidset(inti); }; voidC: : set(inti) { next=i;//具有二义性 } 虚基类的多重继承 classP { public: intnext; }; classA: virtualpublicP { }; classB: virtualpublicP { }; classC: publicA,publicB { public: voidset(inti); }; voidC: : set(inti) { next=i;//无二义性 } 虚基类构造函数的调用规则: <1>同一层次中虚基类的构造函数在非虚基类之前调用 <2>同一层次中包含多个虚基类,虚基类构造函数按照他们的说明顺序调用 <3>若虚基类由非虚基类派生,则遵守先调用基类构造函数,再调用派生类构造函数。 例: 如下所示的类继承关系: 建立toplevel类对象top时,构造函数的调用顺序是: base类 base2类 level2类 base2类 level1类 toplevel类 第三章多态性和虚函数 3.1多态性 3.1.1编译时的多态性——重载 重载的特征是: 各个函数的名字相同,但参数个数或参数的类型不同。 在派生类中调用基类的重载函数时,必须带有基类名和域作用符(: : ),否则引起派生类函数的无限循环调用。 <1>在一个类中进行重载 <2>基类成员函数在派生类中重载 <3>类外部函数的重载 在C++中,大多数系统预定义的运算符都可以重载。 除以下运算符: .: : .*? : sizeof classpoint{ intx,y; public: point(intx,inty); pointoperator+(pointp);//重载运算符+ pointoperator-(pointp);//重载运算符- pointoperator=(pointp);//重载运算符= } 重载不能建立新的运算符,也不能改改变运算符的优先级、结合性和操作数的个数。 赋值运算符(=)和地址运算符(&)无需重载就可以用于每一个类。 3.1.2运行时的多态性——虚函数 多态性是通过虚函数实现的,体现在: 通过基类指针(或引用)请求使用虚函数时,C++会在与对象关联的派生类中正确的选择重定义的函数。 派生类指针调用成员函数时,不是多态性行为;以对象与“.”符调用虚函数,也不是多态性行为。 对象指针: <1>可以让一个指向基类的指针指向公有派生的对象,但是不能指向一个私有派生的对象。 <2>不能将一个指向派生类的指针指向基类对象 <3>声明为基类对象的指针,当指向派生类对象时,只能利用它
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- StudyCC