C++知识点总结

发布时间 2023-12-23 20:10:20作者: jasony_sam

第二章

1.基本数据类型

int有16位,即两个字节,char只占一个字节。在Visual C++ 6.0中,对float提供6位有效数字,对double提供15位有效数字,并且float和double的数值范围不同。对float分配4个字节,对double和long double分配8个字节。

2.常变量

关键字const。变量的值在程序运行期间不能改变。

在定义常变量时必须同时对它初始化

const int a=3;

3.运算

4.自增,自减

++i:在使用i之前先加1

i++:在使用i之后,使i的值加1

--i,i--同理

第四章

1.函数的重载

同一函数名定义多个函数,这些函数的参数个数和参数类型不同

2.函数模板

template <typename T>        //模板声明,其中T为类型参数
T max(T a,T b,T c)          //定义一个通用函数,用T作虚拟的类型名
{
	if(b>a) a=b;
 	if(c>a) a=c;
 	return a;
}

3.有默认参数的函数

void f2(float a,int c,int b=0, char d=′a′);
//实参与形参的结合是从左至右顺序进行的。因此指定默认值的参数必须放在形参表列中的最右端,否则出错

4.const指针

1)指向常量的指针变量——禁写间接引用(只可读)

const int *p;
//程序中不能出现诸如"*p="的语句,但可以对指针p进行改写

int a=3,b=5;
const int *pa=&b;
pa=&a;//指针变量可以重新赋值
cout<<*pa<<endl;
a=100;//变量可以重新赋值
cout<<*pa<<endl;  //输出100

不可以通过指针对变量重新赋值

2)常指针——指针变量的指向不能改变

int r=6;
int * const pr=&r;
//则指针pr被禁写,即pr将始终指向一个地址,成为一个指针常量。
//一定要在定义的时候赋初值。它将不能再作为左值而放在赋值号的左边

//虽然指针被禁写,但其间接引用并没有被禁写。即可以通过pr对r赋值。
*pr=8;

3)指向常量的常指针——禁写指针又禁写间接引用

const int *const px=&x;
//在定义时必须赋初值

5.引用

引用是一种新的变量类型,它的作用是为一个变量起一个别名

int a;
int &b=a;//声明b是a的引用,即b是a的别名。这样a和b的作用相同,代表同一变量
//b是引用类型,不需要额外存储空间,b和a占同一个存储单元

在声明一个引用后,不能再使之作为另一变量的引用。

不能建立引用的引用,也没有引用的指针;

int  &c=b;//试图建立引用的引用,错误
int  *p=b;//错误,不能建立指向引用的指针

可以用来做函数参数,比如

void swap(int &a,int &b) //形参是引用类型
{
    int temp;
	temp=a;a=b;b=temp;
}

函数返回值为引用类型

int a=4;
//相当于返回了一个变量
int &f(int x){
    a=a+x;
    return a;
}//函数返回a的引用,即a的别名

int main(){
    int t=5;
    f(t)=20;//先调用,再赋值;a=20,t=5
    t=f(t);//先调用,再赋值;a=25,t=25
}

由于函数调用返回的引用类型是在函数运行结束后产生的,所以函数不能返回自动变量和形参

第八章

不指定系统默认private。

每个对象所占用的存储空间只是该对象的数据部分所占用的存储空间,而不包括函数代码所占用的存储空间。

C++为此专门设立了一个名为this的指针,用来指向不同的对象,函数代码能够分别对不同对象中的数据进行操作。

1.对象成员的引用

1)通过对象名和成员运算符访问对象中的成员

对象名.成员名

stud1.num=1001; 

2)通过指向对象的指针访问对象中的成员

Time t,*p;
p=&t;
cout<<p->hour;
//在p指向t的前提下,p->hour,(*p).hour和t.hour三者等价

3)通过对象的引用来访问对象中的成员

Time t1;
Time &t2=t1;
cout<<t2.hour;

第九章

1.构造函数

建立对象时自动执行构造函数

构造函数的名字必须与类名同名

(声明构造函数时,形参名可以省略)

1)用参数初始化表对数据成员初始化

Student(int n, char s, nam[ ]): num(n),sex(s){strcpy(name, nam);}

2)重载/使用默认参数

方法同第四章

2.析构函数

当对象的生命期结束时,会自动执行析构函数

先构造的后析构,后构造的先析构;先进后出

3.对象指针

1)指向对象

2)指向对象数据成员

3)指向对象成员函数

class Time{
    public:
    	void get_time();
}

int main(){
    void(Time::*p3)();
    p3=&Time::get_time;//定义指向Time类公用成员函数的指针变量p3
    (t1.*p3)();
}

4)this指针

在每一个成员函数中都包含一个特殊的指针,它的值是当前被调用的成员函数所在对象的起始地址

4.常对象

常对象必须要有初值

Time const t1(12,34,46);

如果一个对象被声明为常对象,则不能调用该对象的非const型的成员函数

修改常对象中的某个数据成员的值,需要对该数据成员声明为mutable

mutable int count;//可以用声明为const的成员函数来修改它的值

5.常对象成员

1)常数据成员

只能通过构造函数的参数初始化表对常数据成员进行初始化

常对象的数据成员都是常数据成员,因此常对象的构造函数只能用参数初始化表对常数据成员进行初始化

2)常成员函数

void get_time( ) const;  //注意const的位置在函数名和括号之后

如果将成员函数声明为常成员函数,则只能引用本类中的数据成员,而不能修改它们,例如只用于输出数据

常成员函数可以引用const数据成员,也可以引用非const的数据成员

const数据成员可以被const成员函数引用,也可以被非const的成员函数引用

常成员函数不能调用另一个非const成员函数

6.指向对象的常指针

Time * const ptr1;  //const位置在指针变量名前面,规定ptr1的值是常值
ptr1=&t1;   //ptr1指向对象t1,此后不能再改变指向

7.指向常对象的指针变量

指向常变量的指针变量可以指向const和非const型的变量,而指向非const型变量的指针变量只能指向非const的变量

8.对象的常引用

void fun(const Time &t);

10.静态成员

如果想在同类的多个对象之间实现数据共享,也不要用全局对象,可以用静态的数据成员

class Box{
    private:
    	static int height;//静态数据成员在内存中只占一份空间。每个对象都可以引用这个静态数据成员
    public:
    	static int volume;
    	//静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员
}

1)静态数据成员

即使不定义对象,也为静态数据成员分配空间,它可以被引用

不能用参数初始化表对静态数据成员初始化

2)静态成员函数

静态成员函数没有this指针

静态成员函数可以直接引用本类中的静态数据成员

11.友元

友元(friend) 可以访问与其有好友关系的类中的私有成员

1)友元函数

class T{
    public:
    	friend void disp(){
            cout<<hour;
        }
    private:
    	int hour;
}

2)友元类

可以将一个类(B类)声明为另一个类(A类)的“朋友”

友元类B中的所有函数都是A类的友元函数,可以访问A类中的所有成员

友元的关系不能传递

第十章

重载不能改变运算符运算对象(即操作数)的个数。重载不能改变运算符的优先级别。重载不能改变运算符的结合性。

1.双目运算符

class Complex{
    private:
    	double real,imag;
    public:
    	Complex operator + (Complex &c2);
    	friend Complex operator + (Complex &c1,Complex &c2);
    	friend bool operator > (Complex &c1,Complex &c2);
}

Complex Complex::operator + (Complex &c2){
    Complex tmp;
    tmp.real=real+c2.real;
    tmp.imag=imag+c2.imag;
    return tmp;
}
//c3=c1+c2;编译系统解释为c1.operator+(c2)
//要求运算表达式第一个参数(即运算符左侧的操作数)是一个类对象,而且与运算符函数的类型相同

Complex operator + (Complex &c1,Complex &c2){
 //定义作为友元函数的重载函数
    return Complex(c1.real+c2.real, c1.imag+c2.imag);
}
//形参的顺序任意。但在使用运算符的表达式中,要求运算符左侧的操作数与函数第一个参数对应,运算符右侧的操作数与函数的第二个参数对应

bool operator > (Complex &c1,Complex &c2){
    if(c1.real>c2.real) return true;
    return false;	
}

2.单目运算符

class Time{
	public:
		Time operator++( ); //前置,++t
    	Time operator++(int);//后置,t++
	private:
       int minute;
	   int sec;
};

Time Time∷operator++( ){ 
    if(++sec>=60){
		sec-=60;
		++minute;
		}
    return *this;
}  
Time Time∷operator++(int)
{
	Time temp(*this);//临时对象
	sec++;
	if(sec>=60)
	{
        sec-=60;
		++minute;
    }
	return temp; //返回的是自加前的对象
}

3.重载流插入运算符和流提取运算符

class Complex{
    private:
    	double real,imag;
    public:
    	friend ostream& operator << (ostream&,Complex&);
}

ostream& operator << (ostream& output,Complex& c){
    output<<"("<<c.real<<"+"<<c.imag<<")"<<endl;
    return output;//连续向输出流插入信息
}

int main(){
    Complex c1;
    cout<<c1;
}

4.不同类型数据间的转换

Complex(double r) {real=r;imag=0;}
//转换构造函数
operator double( ) {return real;} 
//类型转换函数

int main(){
    double d;
    Complex c1,c2;
    d=c1+c2;
}

在已定义了相应的转换构造函数情况下,将运算符“+”函数重载为友元函数,在进行两个复数相加时,可以用交换律

第十一章

派生类中的成员包括从基类继承过来的成员和自己增加的成员两大部分

1.派生类构造函数

不仅可以利用初始化表对构造函数的数据成员初始化,而且可以利用初始化表调用派生类的基类构造函数

Student1(int n, string nam,char s,int a, string ad):Student(n,nam,s),age(a),addr(ad){}

在建立一个对象时①派生类构造函数先调用基类构造函数;②再执行派生类构造函数本身

定义派生类构造函数的一般形式为
派生类构造函数名(总参数表列): 基类构造函数名(参数表列),子对象名(参数表列)

Student1(int n, string nam,int n1, string nam1,int a, string ad):monitor(n1,nam1),Student(n,nam)

执行派生类构造函数的顺序是:① 调用基类构造函数,对基类数据成员初始化;② 调用子对象构造函数,对子对象数据成员初始化;③ 再执行派生类构造函数本身,对派生类数据成员初始化.

如果在基类中没有定义构造函数,或定义了没有参数的构造函数,那么在定义派生类构造函数时可不写基类构造函数。调用派生类构造函数时系统会自动首先调用基类的默认构造函数

2.多重继承

声明多重继承的方法

class D:public A,private B,protected C
{
    //类D新增加的成员
}

多重继承派生类的构造函数各基类的排列顺序任意

3.多重继承引起的二义性问题

1)两个基类有同名成员

可以用基类名来限定

1)两个基类和派生类三者有同名成员

基类的同名成员在派生类中被屏蔽,成为“不可见”的(派生类新增加的同名成员覆盖了基类中的同名成员)

如果只有函数名相同而参数不同,不会发生同名覆盖,而属于函数重载

3)如果类A和类B是从同一个基类派生的

通过类N的直接派生类名来指出要访问的是类N的哪一个派生类中的基类成员

4.虚基类

虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的

class A{};
class B:virtual public A{};
class C:virtual public A{};

经过这样的声明后,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次

虚基类的构造函数不会被调用多次

5.基类与派生类的转换

基类与派生类对象之间有赋值兼容关系

1)派生类对象可以向基类对象赋值

A a1;     //定义基类A对象a1
B b1;     //定义类A的公用派生类B的对象b1
a1=b1;//用派生类对象b1对基类对象a1赋值

在赋值时舍弃派生类自己的成员

2)派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化

A& r=b1;

此时r并不是b1的别名。它只是b1中基类部分的别名,r与b1中基类部分共享同一段存储单元

3)如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象

4)派生类对象的地址可以赋给指向基类对象的指针变量,也就是说,指向基类对象的指针变量也可以指向派生类对象。

基类对象的指针,只能访问派生类中的基类成员,而不能访问派生类增加的成员

第十二章

1.虚函数

虚函数的作用是允许在派生类中重新定义与基类同名的函数,并可以通过基类指针或引用来访问基类和派生类中的同名函数(动态多态性)

Class CA {virtual f(int)};
Class CB: public CA {f(int)};
Class CC: public CA {f(int)};

int main(){
    CA *pt=Null;
	CB B;
	CC C;
	pt=&B;
	pt->f(int);//调用CB中的f函数
	pt=&C;
	pt->f(int); //调用CC中的f函数
}

虚函数的使用方法是:

(1) 在基类用virtual声明成员函数为虚函数,在派生类中重新定义此函数,为它赋予新的功能,并能方便地被调用

(2) 在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。派生类重新声明该虚函数时,可以加virtual,也可以不加

(3) 定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的对象

(4) 通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数

2.静态关联和动态关联

函数重载和通过对象名调用的虚函数,在编译时即可确定其调用的虚函数属于哪一个类,其过程称为静态关联

Test(CA *pt){
    pt->f(int);//不知道函数名和哪个类对象关联
}

int main(){
    CB B;
    Test(&B);//只有在运行阶段才知道如何绑定,称为动态关联
}

3.什么情况下声明虚函数

虚函数只能用于类的继承层次结构中,只能用virtual声明类的成员函数,而不能将类外的普通函数声明为虚函数

当一个类带有虚函数时,编译系统会为该类构造一个虚函数表(virtual function table),它是一个指针数组,存放每个虚函数的入口地址

4.虚析构函数

Class CA{}; 
Class CB: public CA;  
CA *pt = new CB;

在程序用带指针参数的delete运算符撤销对象时,会发生一个情况: 系统会只执行基类的析构函数,而不执行派生类的析构函数

将基类的析构函数声明为虚析构函数即解决(基类的析构函数为虚函数时,无论指针指的是同一类族中的哪一个类对象,系统会采用动态关联,调用相应的析构函数)

5.纯虚函数

纯虚函数是在声明虚函数时被“初始化”为0的函数。

virtual float area( ) const = 0; //纯虚函数
//一般形式为virtual 函数类型 函数名 (参数表列) =0;

如果在一个类中声明了纯虚函数,而在其派生类中没有对该函数定义,则该虚函数在派生类中仍然为纯虚函数

6.抽象类

凡是包含纯虚函数的类都是抽象类,包含纯虚函数的类是无法建立对象的

如果在抽象类所派生出的新类中对基类的所有纯虚函数进行了定义,那么这些函数就被赋予了功能,可以被调用。这个派生类就不是抽象类,而是可以用来定义对象的具体类

如果在派生类中没有对所有纯虚函数进行定义,则此派生类仍然是抽象类,不能用来定义对象

可以定义指向抽象类数据的指针变量,当派生类成为具体类之后,就可以用这种指针指向派生类对象,然后通过该指针调用虚函数,实现多态性的操作

十三章

在C++中,输入输出流被定义为类。C++的I/O库中的类称为流类。用流类定义的对象称为流对象

1.iostream

1)头文件

iostream包含了对输入输出流进行操作所需的基本信息。
fstream用于用户管理的文件的I/O操作。
strstream用于字符串流I/O。
stdiostream用于混合使用C和C++的I/O机制时。
iomanip在使用格式化I/O时应包含此头文件。

2)流对象

对象 含义
cin 标准输入流
cout 标准输出流
cerr 标准错误流
clog 标准错误流

cerr是不经过缓冲区,直接向显示器上输出有关信息,而clog中的信息存放在缓冲区中,缓冲区满后或遇endl时向显示器输出

3)格式输出

书中表13.3

流对象的成员函数

书中表13.4

2.标准输出流

1)cin流

2)字符输入

cin.get()=getchar()

cin.get(ch)

cin.get(字符数组,字符个数n,终止字符)
cin.get(字符指针,字符个数n,终止字符)

如果读取成功则函数返回非0值(真),如失败(遇文件结束符) 则函数返回0值(假)。

cin.getline(字符数组(或字符指针),字符个数n,终止标志字符)

3)其他成员

eof:如果到达文件末尾(遇文件结束符Ctrl+Z),eof函数值为非零值(表示真),否则为0(假)

peek:peek函数的作用是观测下一个字符。

putback:其调用形式为 cin.putback(ch);
其作用是将前面用get或getline函数从输入流中读取的字符ch返回到输入流,插入到当前指针位置,以供后面读取

ignore:其调用形式为 cin.ignore(n, 终止字符)
函数作用是跳过输入流中n个字符,或在遇到指定的终止字符时提前结束(此时跳过包括终止字符在内的若干字符)。

ignore( )//n默认值为1,终止字符默认为EOF

3.文件操作与文件流

1)文件类与文件流对象

ifstream类,从istream类派生的。 用来支持从磁盘文件的输入。
ofstream类,从ostream类派生的。 用来支持向磁盘文件的输出。
fstream类,从iostream类派生的。 用来支持对磁盘文件的输入输出

文件流对象是用文件流类定义的:

ofstream outfile;//定义ofstream类(输出文件流类)对象outfile

需要在使用时加以指定它向哪一个磁盘文件输出

2)文件的打开与关闭

打开:

outfile.open(″f1.dat″,ios::out);  //使文件流与f1.dat文件建立关联
//一般形式为:文件流对象.open(磁盘文件名,输入输出方式);

可以在定义文件流对象时指定参数,调用文件流类的构造函数来实现打开文件的功能

ostream outfile(″f1.dat″,ios::out);

可以用“位或”运算符“|”对输入输出方式进行组合。

关闭:

outfile.close( );//将输出文件流所关联的磁盘文件关闭

所谓关闭,实际上是解除该磁盘文件与文件流的关联

3)对ascii文件的操作

如果文件的每一个字节中均以ASCII代码形式存放数据,即一个字节存放一个字符,这个文件就是ASCII文件

用流插入运算符“<<”和流提取运算符“>>”输入输出标准类型的数据。
用文件流的put,get,getline等成员函数进行字符的输入输出。

4)对二进制文件的操作

对二进制文件的操作也需要先打开文件,用完后要关闭文件。在打开时要用ios::binary指定为以二进制形式传送和存储。

1>用成员函数read和write读写二进制文件

istream& read(char *buffer, int len);
ostream& write(const char * buffer, int len);

int main(){
    a. write(p1,50); //将p1指向的单元开始的50个字节的内容写到a关联的磁盘文件
	b. read(p2,30); //从b所关联的磁盘文件中,读入30个字节(或遇EOF结束),存放在p2所指的一段空间
}

2>与文件指针有关的流成员函数

在磁盘文件中有一个文件指针,用来指明当前应进行读写的位置

“参照位置”可以是下面三者之一:
ios::beg文件开头(beg是begin的缩写),这是默认值。
ios::cur指针当前的位置(cur是current的缩写)。
ios::end文件末尾。

3>随机访问二进制数据文件

可以利用上面的成员函数移动指针,随机地访问文件中任一位置上的数据,还可以修改文件中的内容

4.字符串流

字符串流以内存中用户定义的字符数组(字符串)为输入输出的对象。字符串流也称为内存流

在字符数组中可以存放字符,也可以存放整数、浮点数以及其他类型的数据。
向字符数组存入数据:二进制形式转换为ASCII代码 ,存放在缓冲区,再从缓冲区送到字符数组。
从字符数组读数据:字符数组中的数据送到缓冲区,将ASCII代码转换为二进制形式

字符串流类有istrstream,ostrstream和strstream

字符串流对象关联的不是文件,而是内存中的一个字符数组,因此不需要打开和关闭文件

ostrstream::ostrstream(char *buffer, int n, int mode=ios::out);
ostrstream  strout(ch1, 20);
//建立输出字符串流对象strout,并使strout与字符数组ch1关联(通过字符串流将数据输出到字符数组ch1),流缓冲区大小为20

istrstream::istrstream(char *buffer);
istrstream::istrstream(char *buffer,int n);
istrstream strin(ch2); 
//建立输入字符串流对象strin,将字符数组ch2中的全部数据作为输入字符串流的内容。
istrstream strin(ch2,20);
//流缓冲区大小为20,因此只将字符数组ch2中的前20个字符作为输入字符串流的内容。

strstream::strstream(char *buffer,int n,int mode);
strstream strio(ch3,sizeof(ch3),ios::in|ios::out);
//建立输入输出字符串流对象,以字符数组ch3为输入输出对象