第一章 类和对象
一、知识点:
1 类和对象的概念
1.1 类的定义
1.2 对象的定义
1.3 成员的访问权限
2 构造函数和析构函数
2.1 构造函数的定义
2.2 重载构造函数
2.3 构造函数的调用方式
2.4 析构函数的定义
2.5 析构函数的调用方式
3 对象指针和对象数组
3.1 对象指针的定义和使用
3.2 对象数组的定义和使用
4 成员指针
4.1 数据成员指针的定义和使用
4.2 成员函数指针的定义和使用
5 静态成员
5.1 静态数据成员的定义和使用
5.2 静态成员函数的定义和使用
6 嵌套类
6.1 嵌套类的定义和使用
6.2 嵌套类中构造函数的调用顺序
7 类和对象的应用
二、内容
1 类的定义
2 访问权限
类成员的三类访问权限:
public:说明为public的成员可被程序中的任何代码访问。
private:说明为private的成员只能被类本身的成员函数及友元类的成员函数访问(派生类不能访问)。
protected:说明为protected的成员可被类本身的成员函数、友元类的成员函数及派生类不能访问;但不能被其它(比如main)访问。
3 对象的定义格式
4 构造函数
构造函数可以被重载;构造函数是唯一不能被显式调用的成员函数。
例子1:
...
void main()
{
sample s(2,3),*p=&s;
p->disp();
}
说明:定义对象指针p时不会调用构造函数;p->disp()等价于s.disp()。
例子2(常量对象):
...
void main()
{
Sample(2,3);
}
说明:上述常量对象的作用域不是整个main函数,而仅限于包含该常量的值表达式,表达式一旦计算完成,其中的对象就按构造的逆序析构。
5 析构函数
析构函数不能重载,一个类只能定义一个析构函数;析构函数既可被显式调用,也可被隐式调用(系统自己调用默认析构函数)。
当对象超出其定义范围时(即释放该对象时),编译器自动调用析构函数。在以下情况,析构函数也会被自动调用:
(1) 如果一个对象被定义在一个函数体内,则当这个函数结束时,该对象的析构函数被自动调用。
(2) 当一个对象是使用new运算符动态创建的,在使用delete运算符删除它时,delete将会自动调用析构 函数。
例子1(显式调用析构函数):
#include <iostream.h>
class Sample
{
int x,y;
public:
Sample(){x=y=0;}
Sample(int a,int b){x=a;y=b;}
~Sample()
{
if(x==y)
cout<<"x=y"<<endl;
else
cout<<"x!=y"<<endl;
}
void disp()
{
cout<<"x="<<x<<",y="<<y<<endl;
}
};
void main()
{
Sample s1(2,3);
s1.disp();
s1.~Sample();
}
程序输出为:
x=2,y=3
x!=y
x!=y
说明:上面最后一句s1.~Sample(); 显示调用了析构函数一次,最后由于s1对象是在main函数中定义的,当main函数结束时系统又调用了一次
析构函数,所以共调用了两次析构函数,输出两个x!=y
例子2:
#include <iostream.h>
#include <stdlib.h>
class Sample
{
public:
int x,y;
Sample(){x=y=0;}
Sample(int a,int b){x=a;y=b;}
void disp()
{
cout<<"x="<<x<<",y="<<y<<endl;
}
~Sample()
{
if(x==y)
cout<<"x=y"<<endl;
else
cout<<"x!=y"<<endl;
}
};
void main()
{
Sample s1(2,3);
s1.disp();
if(s1.x==2)
exit(0);
}
程序输出:x=2,y=3
说明:程序非正常退出,不会隐式调用析构函数。
6 类成员指针
(1) 类数据成员指针
(2) 类成员函数指针
(3)this指针:当调用一个类的成员函数时,this指针被初始化为被调用函数所在的类实例的地址,this指针用于返回当前对象自身。
例子1(对象指针数组):
...
void main()
{
Sample s1,s2(1,2),s3(10,20);
Sample *pa[3]={&s1,&s2,&s3};
for(int i=0;i<3;i++)
{
pa[i]->disp();
}
}
说明:上面对象数组赋值Sample *pa[3]={&s1,&s2,&s3};也可以这么赋值:
Sample *pa[3];
pa[1]=&s1;
pa[2]=&s2;
pa[3]=&s3;
例子2(类数据成员指针):
#include <iostream.h>
class Sample
{
public:
int x;
int y;
void disp()
{
cout<<"x="<<x<<",y="<<y<<endl;
}
};
void main()
{
int Sample::*pc;//定义Sample类的数据成员指针
Sample s;
pc=&Sample::x;
s.*pc=10;//等价于s.x=10
pc=&Sample::y;
s.*pc=20;//等价于s.y=20
s.disp();
}
程序输出为:x=10,y=20
说明:Sample类的数据成员x和y必须是public的,否则main方法不能访问修改。
例子3(类成员函数指针);
#include <iostream.h>
class Sample
{
int x;
int y;
public:
Sample(int a,int b){x=a;y=b;}
int getx(){return x;}
int gety(){return y;}
};
void main()
{
int (Sample ::*fp)();//定义类成员函数指针
fp=Sample::getx;
Sample s(2,5);
int v=(s.*fp)();//等价于s.getx();
fp=Sample::gety;
int t=(s.*fp)();
cout<<"v="<<v<<",t="<<t<endl;
}
程序输出:
v=2,t=5
7 对象数组
数组元素为对象的数组。
8 静态成员
(1) 静态数据成员:对于多个对象(同一个类)来说,静态数据成员只存储在一处,共用。
(2) 静态成员函数:属类的静态成员,不是对象成员。静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静
态成员。 如果静态成员函数中要引用非静态成员时,可通过对象来引用。
例子1(静态对象):
#include <iostream.h>
class Sample
{
int x;
public:
Sample(int a)
{
x=a;
cout<<"constructing object:x="<<x<<endl;
}
};
void func(int n)
{
static Sample obj(n);
}
void main()
{
func(1);
func(10);
}
程序输出为:constructing object:x=1
说明:obj对象是静态对象,和静态变量一样,所以只在第一次进入func函数时构造该对象。
例子2(静态数据成员&静态成员函数):
#include <iostream.h>
class Sample
{
int A;
static int B;//静态数据成员
public:
Sample(int a){A=a;B+=a;}
static void func(Sample s);//静态成员函数
};
void Sample::func(Sample s)
{
cout<<"A="<<s.A<<",B="<<B<<endl;
}
void Sample::B=0;
void main()
{
Sample s1(2),s2(5);//执行完这一行,B=7
Sample::func(s1);
Sample::func(s2);
}
程序输出:
A=2,B=7
A=5,B=7
说明:func函数中s.A属于静态成员函数中调用类的非静态数据成员,所以加对象s;
后面的B是静态数据成员,所以可以直接写B(当然,也可以写成s.B)。
9 嵌套类
在一个类中定义的类称为嵌套类,定义嵌套类的类称为外围类。
--------------------------------------------------
第二章 引用
一 知识点
1 引用的概念
1.1 变量引用
2.2 对象引用
2 拷贝构造函数
3 引用与函数的关系
3.1 变量引用作为函数参数
3.2 对象引用作为函数参数
二 内容概要
1 引用的概念
引用是个别名,对引用的改动实际就是对目标的改动。
2 拷贝构造函数
拷贝构造函数实际上也是构造函数,它是在初始化时被调用来将一个已知对象的数据成员的值拷贝给正在创建的另一个同类的对象。拷贝构造函数的格式如下:
<类名>::<拷贝构造函数>(const <类名>&<引用名>)
const是一个类型修饰符,其修饰的对象是一个不能被更新的常量。
3 引用作为函数参数
引用具有指针的威力,但是调用引用传递的函数时,可读性却比指针传递好;使用对象引用作为函数参数要比对象指针作为函数参数更普遍,更简单、更直接。
例1
void swap(int *p1,int *p2)
{
int p;
p=*p1;
*p1=*p2;
*p2=p;
}
void swap(int &x,int &y)
{
int temp;
temp=x;
x=y;
y=temp;
}
void swap(char *&x,char *&y)
//注:本函数使用引用,由于字符串指针本身就是地址,所以这里可以不使用引用参数,效果是一样的
{
char *temp;
,。 temp=x;
x=y;
y=temp;
}
----------------------------------------------------
第三章 友元
一 知识点
1 友元函数
1.1 友元函数的概念
1.2 友元函数的定义
1.3 友元函数的使用
2 友元类
2.1 友元类的概念
2.2 友元类的定义
2.3 友元类的使用
二 内容概要
1 友元函数
friend <类型> <友元函数> (<参数表>);
友元说明的位置可在类的任何部位,既可在public区,也可在protected区,意义完全一样。友元函数定义则在类的外部,一般与类的成员函数定义放在一起。
类的友元函数可以直接访问该类的所有成员,但它不是成员函数,可以象普通函数一样在任何地方调用。
例1
#include <iostream.h>
class Sample
{
int n;
public:
Sample(int n){n=i;}
friend int add(Sample &s1,Sample &s2);
};
int add(Sample &s1,Sample &s2)
{
return s1.n+s2.n;
}
void main()
{
Sample s1(10),s2(20);
cout<<add(s1,s2)<<endl;
}
注:友元函数不是类的成员函数,可在任何地方调用。
2 友元类
class A
{
...
public:
friend class B;
...
}
经上述说明后,类B的所有成员函数都是类A的友元函数,可以访问A中的所有成员。
--------------------------------------------------
第四章 重载
一 知识点
1普通函数重载
1.1普通函数重载的定义
1.2普通函数重载的调用
2成员函数重载
2.1成员函数重载的定义
2.2成员函数重载的调用
3运算符重载
3.1单目运算符重载
3.2双目运算符重载
3.3等号运算符重载
3.4new和delete运算符重载
3.5下标运算符重载
二 内容概要
1 普通函数重载
函数名字相同,但是函数在参数个数或者参数类型上不同。
2 成员函数重载
3 运算符重载
运算符重载实质上就是函数重载。
例1
#include <iostream.h>
class Sample
{
int n;
public:
Sample() {}
Sample(int i) {n=i;}
Sample & operator = (Sample);
void disp(){cout<<"n="<<n<<endl;}
};
Sample &Sample::operator=(Sample s)
//这里&代表:调用后返回*this的引用(即题中执行后为s2的引用)给s2;
//若将此两个"&"去掉也可以,将直接返回对象*this给s2
{
Sample::n=s.n;
return *this;
}
void main()
{
Sample s1(10),s2;
s2=s1;//等价于s2.operator=(s1)
s2.disp();
}
例2
#include <iostream.h>
{
int n;
public:
Sample(int i){n=i;}
operator ++() {n++;}//前缀重载运算符
operator ++(int) {n+=2;}//后缀重载运算符
void disp()
{
cout<<"n="<<n<<endl;
}
};
void main()
{
Sample A(2),B(2);
A++;//调用后缀重载运算符
++B;//调用前缀重载运算符
A.disp();
B.disp();
}
---------------------------------------------------
第五章 模板
一 知识点
1 函数模板
1.1 函数模板的定义
1.2 函数模板的调用
2 类模板
2.1 类模板的定义
2.2 类模板产生模板类
二 内容概要
1 函数模板
当编译系统发现有一个函数调用:<函数名>(<实参表>);将根据<实参表>中的类型,生成一个重载函数即模板函数。
对模板函数的说明和定义必须实全局作用域。模板不能被说明为类的成员函数。
例1
#include <iostream.h>
template <class T>
T max(T x,T y)
{
return (x>y?x:y);
}
vid main()
{
cout<<max(2,5)<<","<<max(3.5,2.8)<<endl;
}
2 类模板
例2
#include <iostream.h>
template <class T>
class Sample
{
T n;
public:
Sample(T i) {n=i;}
void operator++();//重载
void disp(){cout<<"n="<<n<<endl;}
};
template <class T>
void Sample<T>::operator ++()
{
n+=1;//不能用n++,因为double型不能用++
}
void main()
{
Sample<char> s('a');
s++; //即s.operator++()
s.disp();
}
例3 编写一个函数模板,它返回两个值中的较小者,同时要求能正确处理字符串。
解:这里设计一个函数模板template<class T>T min(T a,T b),可以处理int、float和char等数据类型,为了能正确字符串,添加一个重载函数专门处理字符串比较,即char
*min(char *a,char *b)。本题程序如下:
#include <iostream.h>
#include <string.h>
template <class T>
T min(T a,T b)
{
return (a<b?a:b);
}
char *min(char *a,char *b)
{
return (strcmp(a,b)<0?a:b);
}
void main()
{
double a=3.56,b=8.23;
char s1[]="Hello",s2[]="Good";
cout<<"输出结果:"<<endl;
cout<<" "<<a<<","<<b<<"中较小者:"<<min(a,b)<<endl;
cout<<" "<<s1<<","<<s2<<"中较小者:"<<min(s1,s2)<<endl;
}
注:编译时,首先匹配重载函数,然后再寻找模板的匹配。
-----------------------------------------------------
第六章 继承和派生
一 知识点
1 继承和派生的概念
1.1派生的定义方法
1.2派生的继承方式
1.3基类与派生类的关系
2 单继承
2.1单继承的概念
2.2构造函数与析构函数调用顺序
2.3成员访问权限的控制
3多继承
3.1多继承的概念
3.2多继承中构造函数与析构函数调用顺序
3.3多继承中二义性问题
4虚基类
4.1虚基类的概念
4.2虚基类的构造函数
二 内容概要
1 派生类
2 单继承
class<派生类名>:<继承方式><基类名>
{
<派生类新定义成员>
};
3 多继承
class<派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,...
{
<派生类新定义成员>
};
4 虚基类
当某类的部分或全部直接基类是从另一个共同基类派生而来时,这些直接基类中从上一级基类继承来的成员就拥有相同的名称。在派生类的对象中,这些同名成员在内存中同时
拥有多个拷贝,如何进行分辨呢?最基本的方法是将直接基类的共同基类设置为虚基类,这时从不同的路径继承过来的该类成员在内存中只拥有一个拷贝,这样就解决了同名成员
的唯一标识问题。
虚基类的初始化,派生类构造函数调用的次序有以下原则:
a 虚基类的构造函数在非虚基类之前调用。
b 若同一层次中包含多个虚基类,这些虚基类的构造函数按它们说明的次序调用。
c 若虚基类由非虚基类派生而来,则仍然先调用基类构造函数,再调用派生类中构造函数额执行顺序。
例1
#include <iostream.h>
class A
{
public:
int n;
};
class B:public A {};
class C:public A {};
class D:public B,public C
{
int getn() {return B::n;}
};
void main()
{
D d;
d.B::n=10;
d.C::n=20;
cout<<d.B::n<<","<<d.C::n<<endl;//输出为:10,20
}
注:D类是从B和C类派生的,而B和C类又都是从A类派生的,但各又自己的副本。所以,对于对象d,d.B::n和d.C::n是两个不同的数据成员,它们互无关系。
例2
#include <iostream.h>
class A
{
public:
int n;
};
class B:virtual public A {};
class C:virtual public A {};
class D:public B,public C
{
int getn() {return B::n;}
};
void main()
{
D d;
d.B::n=10;
d.C::n=20;//这里会覆盖,因为B和C类有一个共用的A类的副本
cout<<d.B::n<<","<<d.C::n<<endl;//输出为:20,20
}
注:例1同例2比较
例3
#include <iostream.h>
class A
{
public:
A(char *s) {cout<<s<<endl;}
~A() {}
};
class B:public A
{
public:
B(char *s1,char *s2):A(s1)
{
cout<<s2<<endl;
}
};
class C:public A
{
public:
C(char *s1,char *s2):A(s1)
{
cout<<s2<<endl;
}
};
class D:public B,public C
{
public:
D(char *s1,char *s2,char *s3,char *s4):B(s1,s2),C(s1,s3)
{
cout<<s4<<endl;
}
};
void main()
{
D d("class A","class B","class C","class D");
}
注:B和C类中各有A类的副本,所以输出为:
class A
class B
class A
class C
class D
例4
#include <iostream.h>
class A
{
public:
A(char *s) {cout<<s<<endl;}
~A() {}
};
class B:virtual public A
{
public:
B(char *s1,char *s2):A(s1)
{
cout<<s2<<endl;
}
};
class C:virtual public A
{
public:
C(char *s1,char *s2):A(s1)
{
cout<<s2<<endl;
}
};
class D:public B,public C
{
public:
D(char *s1,char *s2,char *s3,char *s4):B(s1,s2),C(s1,s3),A(s1)
{
cout<<s4<<endl;
}
};
void main()
{
D d("class A","class B","class C","class D");
}
注:虚基类的初始化由最终派生类(在这里是D)来实现,故本题输出为:
class A
class B
class C
class D
-------------------------------------------------
第七章 多态性
一 知识点
1 静态联编和动态联编
1.1 静态联编和动态联编的区别
1.2 动态联编的实现方法
2 虚函数
2.1 虚函数的概念
2.2 虚函数的定义
2.3 虚函数的使用方法
3 纯虚函数
3.1 纯虚函数的概念
3.2 纯虚函数的定义
3.3 纯虚函数的使用方法
4 抽象类
4.1 抽象类的概念
4.2 抽象类的定义
4.3 抽象类的应用
二 内容概要
1 静态联编和动态联编
这种把函数调用与适当的函数代码相对应的动作,叫做联编。联编分为静态联编和动态联编。在编译阶段决定执行哪个同名的被调用函数,这称为静态联编。而在编译阶段不能
决定执行哪个同名的被调用函数,只在执行阶段才能依据要处理的类型来决定执行哪个类的成员函数,这称为动态联编。
多态性也分为静态和动态两种。静态多态性是指定义在一个类或一个函数中存在的同名函数,它们可根据参数表(类型及个数)区别语义,并通过静态联编实现。例如,在一个
类中定义的不同参数的构造函数及运算符重载函数等。动态多态性是指定义在一个类层次中的不同类中的重载函数,他们一般具有相同的参数表,因而要根据指针指向的对象所在
类来区别语义,它通过动态联编实现。
2 虚函数
如果某类中的一个成员函数被说明为虚函数,这就意味这该成员函数在派生类中可能有不同的实现。
动态联编只能通过指针或引用标识对象来操作虚函数。如果采用一般类型的标识对象来操作虚函数,则将采用静态联编方式调用虚函数。
3 纯虚函数
纯虚函数在虚函数的后面加上“=0”,表示该虚函数无函数体,并非赋值运算。
4 抽象类
对于抽象类的使用有几点规定:
a 抽象类只能用作其它类的基类,不能建立抽象类对象。
b 抽象类不能用作参数类型、函数返回类型或显式转换的类型。
c 可以说明指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。
例1
#include <iostream.h>
class A
{
public:
virtual void disp(){cout<<"class A"<<endl;}
};
class B
{
public:
virtual void disp(){cout<<"class B"<<endl;}
};
class C:public A
{
public:
virtual void disp(){cout<<"class C"<<endl;}
};
void cdisp(A*p)
//动态联编只能通过指针或引用标识对象来操作虚函数。
{
p->disp();
}
void main()
{
B b;
C c;
cdisp(&b);
cdisp(&c);
}
例2
#include <iostream.h>
class base
{
public:
base(){fc();}
virtual void fc(){cout<<"In class base"<<endl;}
virtual ~base()=0{cout<<"Destructing base object"<<endl;}
};
class A:public base
{
public:
A(){fc();}
void f(){fc();}
~A(){fd();}
void fd() {cout<<"Destructing A object"<<endl;}
};
class B:public A
{
public:
B(){}
void fc() {cout<<"In class B"<<endl;}
~B() {fd();}
void fd() {cout<<"Destructing B object"<<endl;}
};
void main()
{
B b;
b.fc();
base *p=new A;
delete p;
}
输出结果:
In class base // B b;
In class base // B b;
In class B // b.fc();
In class base //base *p=new A;
In class base //base *p=new A;
Destructing A object //执行delete p调用析构函数
Destructing base object //执行delete p调用析构函数
Destructing B object //清楚对象b
Destructing A object //清楚对象b
Destructing base object //清楚对象b
---------------------------------------------
第八章 I/O流库
一 知识点
1 I/O流库的概念
2 格式化输入输出
2.1 ios标志位的使用
2.2 格式化输出函数的使用
2.3 操作符的使用
3 磁盘文件的输入输出
3.1 文本文件的读写
3.2 二进制文件的读写
3.3 随机访问数据文件
4 输入输出运算符重载
4.1 插入符的重载
4.2 提取符的重载
二 内容概要
1 I/O流的概念
在c++中,流既可以表示数据从内存传送到某个载体或设备中,即输出流;也可以表示从某个载体或设备传送到内存缓冲区变量中,即输入流。在进行I/O操作时,先打开操作,
使流和文件发生联系,建立联系后的文件才允许数据流入或流出。输入或处处结束后,使用关闭操作使文件与流断开联系。
2 格式化I/O
(1) 使用ios成员函数
(2) 使用I/O操作符
3 文件流I/O
(1) 文件的打开与关闭
(2) 文件的读写
4 I/O运算符重载
(1) 重载"<<"运算符
(2) 重载">>"运算符
例1
#include <iostream.h>
#include <fstream.h>
void main()
{
char ch;
fstream out("abc.txt",ios::in|ios::out);//创建流out与abc.txt建立联系,打开文件abc.txt
out<<"abcdefg";//将"abcdefg"写到流out中
out.seekp(0);
out.get(ch);
while(ch!=EOF)
{
cout<<ch; //cout为终端(显示器)
out.get(ch);
}
cout<<endl;
}
输出:abcdefg
注:fstream派生自iostream
例2
#include <iostream.h>
class Sample
{
int x,y;
public:
Sample(int i,int j){x=i;y=j;}
friend ostream & operator << (ostream &out,Sample &s);
};
ostream & operator<<(ostream &out,Sample &s)
{
cout<<"x="<<s.x<<",y="<<s.y<<endl;
return out;
}
void main()
{
Sample obj(10,20);
cout<<obj;//调用重载运算符函数
}
注:这里设计"<<"运算符虫子函数的格式如下:
ostream &operator<<(ostream &out,<类名> &<对象名>)
{
//这里放代码
return out;
}
-------------------------------------------------
第九章 异常处理
一 知识点
1 异常处理机制
2 异常处理子句
2.1 throw子句
2.2 try子句
2.3 catch子句
二 内容概要
1 异常处理机制
c++语言异常处理机制的基本思想是将异常的检测与处理分离。
2 异常处理子句