c/c++总结

►只有用机器语言编写的程序才能被计算机直接执行,其他任何语 言编写的程序都需要翻译成机器语言。二进制0和1

►按照程序设计语言的发展历程,大致可分为机器语言、汇编语言、 高级语言3类。

C++的目标是——在保证效率与C语言相当的情况下,加强程序的组织性,保证同样功能的程序,C++更短小

48 - 0      65 - A      97 - a

long - 4

float 精确到7为  double精确到16  longdouble精确到19

常量:整型,实型,字符型,字符串型,符号常量

►break语句只能用在switch语句和循环语句(while、do、for)中, 不得单独使用。

continue语句只能用在循环语句(while、do、for)中,不能单独 使用

►当函数的返回类型不是void,表明函数有返回值。如果函数体内没 有return语句,此时函数返回的值是与返回类型相同但内容却是随 机的一个值。

形式参数相关说明:

►1)函数定义时指定的形参,在未进行函数调用前,并不实际占用 内存中的存储单元。

►2)只有在发生函数调用时,形参才分配实际的内存单元,接受从 主调函数传来的数据。

►3)当函数调用结束后,形参占用的内存单元被自动释放。

实际参数:

►函数调用时提供给被调函数的参数称为实际参数(arguments), 简称实参。

►实参必须有确定的值,因为调用函数会将它们传递给形参。实参可 以是常量、变量或表达式,还可以是函数的返回值。

默认参数:

如果在函数定义时设置了默认参数,那么就不能在函数声明 中再次设置,反之亦然。

可以设置多个默认参数,设置的顺序为自右向左,换言之, 要为某个参数设置默认值,则它右边的所有参数必须都是默认参数。

►(3)默认值可以是常量、全局变量,甚至是一个函数调用(调用 实参必须是常量或全局变量的表达式),不可以是局部变量。

►函数重载

是在同一个域中用同一个函数名 来定义多个函数,

但函数参数列表应彼此有不同,或者是参数个数 不同,或者是参数类型不同,或者两者均有不同。

int add(int a, int b); double add(double a, double b);形参类型不同

int add(int a, int b); int add(int a, int b, int c);形参个数不同

►函数重载的使用说明:

►(1)重载函数的形参必须不同(个数不同或类型不同)。

►(2)编译程序将根据实参和形参的类型及个数的最佳匹配来选择 调用哪一个函数。

►(3)不要将不同功能的函数声明为重载函数,以免出现调用结果 的误解、混淆。

►局部变量的说明。

►(1)局部变量只能在定义它的区域及其子区域中使用。

►(2)在同一个区域中不能定义相同名字的变量。

►(3)在不同区域中允许定义相同名字的变量,但本质上它们是不 同的变量

►(4)如果一个变量所处区域的子区域中有同名的变量,则该变量 在子区域无效,有效的是子区域的变量,称为定义屏蔽。

 全局变量(global variable),全局变量的有效区域是从定义变量的位置开始到源文 件结束。

作用域

 (1)规则一。同一个作用域内不允许有相同名字的实体,不同作 用域的实体可以有相同名字。

(2)规则二。实体在包含它的作用域内,从定义或声明的位置开 始,按文件行的顺序往后(往下)直到该作用域结束均是可见的, 包含作用域内的所有子区域及其嵌套。

(3)规则三。若实体A在包含它的作用域内的子区域中出现了相同 名字的实体B,则实体A被屏蔽。

(4)规则四。可以使用extern声明将变量或函数实体的可见区域 往前延伸,称为前置声明

(5)规则五。在全局作用域中,变量或函数实体若使用static修饰, 则该实体对于其他源文件是屏蔽的,称为私有的(private)。

CPU的寄存器来存放局部变量,称为寄存器变量 register,寄存器的读写速度远快于内存的读写速度。

Volatile声明的变量随时会发生改变,不需要进行优化。

使用的位置:状态寄存器,中断服务子程序中会访问的非自动变量,多线程中被几个任务共享的变量。

数组作为函数的参数,传递的是数组的地址。

 这样的传递机制使得当数组作为函数参数时,有下面的特殊性。

(1)由于形参数组就是实参数组,所以在被调函数中使用形参就 是在间接使用实参,这点与变量作为函数参数的情况是不同的。

2)既然形参数组就是实参数组,所以函数调用时不会为形参数组 分配存储空间。

形参数组不过是用数组定义这样的形式来表明它是个数组,能够接 收实参传来的地址,形参数组的长度说明也无实际作用。因此形参 数组甚至可以不用给出长度。

(3)虽然实参数组将地址传到了被调函数中,但被调函数并不知 道实参数组的具体长度,那么假定的大小对于实参数组来说容易数 组越界。

实际编程中可以采用以下方法来解决: ►函数调用时再给出一个参数来表示实参数组的长度。

(4)多维数组作为函数的参数,形参数组第1维可以与实参相同, 也可以不相同;可以是任意长度,也可以不写长度;但其他维的长 度需要相同。

字符串的概念

►C++语言规定字符串是以'\0'(ASCII值为0)字符作为结束符的字符 数组。

►在程序中可以通过判断数组元素是否为空字符来判断字符串是否结 束,换言之,只要遇到数组元素是空字符,就表示字符串在此位置 上结束。

►由于字符串实际存放在字符数组中,所以定义字符数组时数组的长 度至少为字符串长度加1(空字符也要占位)。

选择排序

void SelectionSort(int A[],int n)  

{

     int i,j,k,t;

     for(i = 0;i < n - 1;i++){

     k = i;

     for(j=i+1; j<n; j++) //一趟选择排序

      if (A[j] < A[k]) k=j; //<升序 >降序

       if(i!=k) t=A[i], A[i]=A[k], A[k]=t;

     }

}//先选最小的,再换

二分法查找

int BinarySearch(int A[],int n,int find)

{ //二分查找 n=序列元素个数 find=欲查找数据

    int low,upper,mid;

    low=0 , upper=n-1; //左右两部分

    while(low<=upper) {

        mid = low + (upper-low)/2; //不用(upper+low)/2,避免upper+low溢出

        if( A[mid] < find) low = mid+1; //右半部分

            else if (A[mid] > find) upper = mid - 1; //左半部分

            else return mid; //找到

}

return -1; //未找到

}

int a, *p=&a;

a=100; //直接访问a(对象直接访问)

*p=100; //*p就是a,间接访问a(指针间接访问)

*p=*p+1; //等价于a=a+1

int a, b, *p1=&a, *p2;

&*p1 < - > &a < - > p1

*&a < - > *p1 < - > a

int a, z=0;

int *p1=a; //错误,地址初值不能是变量

p1=z;//错误,整型变量不能作为指针,即使值为0

p1=4000; //错误,整型数据不能作为指针

p1=null; //正确,指针允许0值常量表达式

p1=0; //正确,指针允许0值常量表达式

int a, *p1;

double f, *p3;

p1=&a; //正确

p3=&f; //正确

p1=&f; //错误,p1和&f指向类型不相同

int x, *px=&x; //正确

int *py=px; //正确,相同指向类型的另一个指针

int a[10], *px=&a[2]; //正确

int *py=&a[++i]; //正确,相同指向类型的另一个指针

(3)一个指针曾经指向一个已知对象,在对象的内存空间释放后,

虽然该指针仍是原来的内存地址,但指针所指已是未知对象,称为 “迷途指针”

►设p1、p2是相同类型的两个指针(常量或变量),

则p2-p1的结果 为两个指针之间对象的个数,如果p2的地址值大于p1结果为正, 否则为负

int x[5], *p1=&x[0],*p2=&x[4]; int n; n=p2-p1;//n的值为4

const double pi = 3.14;

double *ptr = &pi; //错误,ptr是非const指针变量

const double *cptr=&pi;//正确,cptr是const指针变量

const double pi = 3.14;

const double *cptrf = &pi; //正确

double f = 3.14; //f是double型,f是非const

cptrf = &f; //正确,允许将f的地址赋给cptrf

f=1.618; //正确,可以修改f的值

*cptrf = 10.1; //错误,不允许通过引用cptrf修改f的值

►通过对象名称直接访问对象,优点是直观,操作哪个对象一目了然, 缺点一个函数内部不能使用另一个函数的局部变量;

►通过指针(或地址)间接访问对象,优点是无所不能,缺点是程序 中大量出现的间接访问,实在分不清具体是哪个对象,需要通过上 下文去分析。

►C++扩充了C语言对象访问方式,提供了引用访问。通过引用访问 对象,结合了按名访问和按地址访问各自的优点,非常适合作为函 数参数。

►在C++中,引用全部是const类型,声明之后不可更改(即不能再 是别的对象的引用)。

1)声明一个引用类型变量时,必须同时初始化它,声明它是哪个对象的别名,即绑定对象。例如:

int &r; //错误,引用是const类型,必须在声明时初始化

int x, &r=x; //正确 声明r是x的引用

►(2)不能有空引用,引用必须与有效对象的内存单元关联

►(3)引用一旦被初始化,就不能改变引用关系,不能再作为其他 对象的引用。例如:

int x, y; //定义整型变量x,y

int &r=x; //正确 声明r是x的引用

int &r=y; //错误 r不能再是别的对象的引用

►(4)指定类型的引用不能初始化到其他类型的对象上,例如:

double f; //定义浮点型变量f

int &r=f; //错误 r值整型的引用,不能绑定到浮点型的对象上

►(5)引用初始化与对引用赋值含义完全不同,例如:

int x; //定义整型变量x

int &r=x; //初始化 指明r是x的引用,即将r绑定到x

r=100; //引用赋值 100赋值到r绑定的内存单元中(即x)

►(6)取一个引用的地址和取一个对象的地址完全一样,都是用取 地址运算。例如:

int x, &r=x; //定义整型变量x,y

int *p1=&x; //p1指向x

int *p2=&r; //p2指向r,本质上指向x

void swap(int &a,int &b)

{

    int t;

    t=a, a=b, b=t;

}

int main()

{

int x=10, y=20;

swap(x,y);

cout<<x<<","<<y;

return 0;

}

引用作为函数返回值

int& max(int &a,int &b)

{ return (a>b? a:b); }

int main()

{ int x=10,y=20,z;

z = max(x,y);

cout << z;

return 0;}

►可以看出,函数返回引用与函数返回值有重大区别,它不是返回一 个临时对象,而是相当于返回实体对象本身。

正因为如此,函数返 回引用可以作为左值。

例如: int& fun(int &a,int &b)

{ return (a>b? a:b); }

int x=10,y=20,z=5;

fun(x,y)=z; //调用fun函数后相当于y=z;

cout << y;

指向函数的指针变量►它要求函数指针变量与指向函数必须有相同的返回类型、参数个数、 参数类型。

►例如假设:

int max(int a, int b); //max函数原型

int min(int a, int b); //min函数原型

int (*p)(int a, int b); //定义函数指针变量

►则 p=max;

►称p指向函数max。它也可以指向函数min,即可以指向所有与它有 相同的返回类型、参数个数、参数类型的函数。

c=p(a,b); //等价于c=max(a,b);

►函数指针的用途

►指向函数的指针多用于指向不同的函数,从而可以利用指针变量调 用不同函数,相当于将函数调用由静态方式(固定地调用指定函数) 变为动态方式(调用哪个函数是由指针值来确定)。

熟练掌握函数 指针的应用,有利于程序的模块化设计,提高程序的可扩展性。

int *p1, *p2;

char *pz1,*pz2;

p1=new int; //分配一个整型空间,若成功则p1指向该空间,否则p1为NULL

p2=new int(10); //分配一个整型空间,且给这个整型赋初值10,即*p2为10

pz1=new char[80]; //分配一个字符数组(字符串)空间,即pz1为字符串指 针

pz2=new char[5][80]; //分配一个二维字符数组(字符串数组)空间,即 pz1为字符串数组指针

delete p1; //释放p1指向的整型空间

delete [] pz1; //释放pz1指向的字符串空间

1)new运算结果是指向分配得到的内存空间的指针,如果没有 足够的内存空间可以分配,其运算结果是一个0值指针。

2)销毁对象后,指针p1变成没有定义,然而它仍然存放先前所 指向的对象(已销毁)的地址,因此指针p1不再有效,称这样的指 针为迷途指针。

通常在delete运算之后将指针重设为0值指针,避 免迷途指针。

3)用new创建的动态对象使用完后,必须用delete销毁它。

4)delete只能删除由new创建的动态对象,否则将导致程序错误。

共用体内存长度是所有 成员内存长度的最大值,结构体内存长度是所有成员内存长度之和。

union B

{ int m; //整型成员

char a,b; //字符成员

short n; //短整型成员

};

1 x.m=5678; //给共用体成员赋值

2 cout<<x.m<<","<<x.n<<","<<x.a<<","<<x.b<<endl; //输出 5678,5678,46,46

3 cin>>x.m>>x.n>>x.a>>x.b;

4 x.n++; //共用体成员运算

►第1句给成员m赋值5678,由于所有成员内存是共享的,因此每个 成员都是这个值。

►第2句输出m和n为5678,输出a和b为46,因为a和b类型为char, 仅使用共享内存中的一部分(4个字节的低字节),即5678 (0x162E)的0x2E(46)。

►由于成员是共享存储空间的,使用共用体对象成员时有如下特点:

►①修改一个成员会使其他成员发生改变,所有成员存储的总是最后 一次修改的结果;

►②所有成员的值是相同的,区别是不同的类型决定了使用这个值的 全部或是部分;

►③所有成员的起始地址值是相同的,因此通常只按一个成员输入、 初始化;

enum DAYS {MON,TUE,WED,THU,FRI,SAT,SUN};

►DAYS是枚举类型,MON等是命名枚举常量。默认时枚举常量总是 从0开始,后续的枚举常量总是前一个的枚举常量加一。如MON为 0,TUE为1,……,SUN为6。

►(1)可以在(仅仅在)声明枚举类型时,为命名枚举常量指定值。 例如:

enum COLORS {RED=10,GREEN=8,BLUE,BLACK,WHITE}; ►则RED为10、GREEN为8、BLUE为9、BLACK为10、WHITE为11。

►(2)命名枚举常量是一个整型常量值,也称为枚举器,在枚举类 型范围内必须是唯一的。命名枚举常量是右值不是左值,例如:

RED=10; //错误,RED不是左值,不能被赋值 GREEN++; //错误,GREEN不是左值,不能自增自减

enum COLORS color;

color=101; //错误,不能类型转换

color=(COLORS)101; //正确,但结果没有定义

►所有成员必须在类的内部声明,一旦类定义完成后,就没有任何其 他方式可以再

类定义一般放在程序文件开头,或者放到头文件中被程序文件包含, 此时这个定义是全局的。在全局作用域内,该定义处处可见,因此 同作用域内的所有函数都可以使用它。

类定义也可以放到函数内部或局部作用域中,此时这个定义是局部 的。若在函数内部有同名的类定义,则全局声明在该函数内部是无 效的,有效的是局部定义的。

►无论数据成员还是函数成员,类的每个成员都有访问控制属性,由 以下三种访问标号说明:public(公有的)、private(私有的)和 protected(保护的)。

►公有成员用public标号声明,类成员和类用户都可以访问公有成员, 任何一个来自类外部的类用户都必须通过公有成员来访问。显然, public实现了类的外部接口。

►私有成员用private标号声明,只有类成员可以访问私有成员,类用 户的访问是不允许的。显然,private实现了私有成员的隐蔽。

►保护成员用protected标号声明,在不考虑继承的情况下, protected的性质和private的性质一致,但保护成员可以被派生类 的类成员访问。

►成员访问控制是C++的类和结构体又一个重要特性。加上访问标号, 类定义更一般的形式为:

class 类名 { //类体 public: //公有访问权限 公有的数据成员和成员函数

protected: //保护访问权限 保护的数据成员和成员函数

private: //私有访问权限 私有的数据成员和成员函数

};

如果没有声明访问控制属性,类所有成员默认为private,即私有的

例子:

class Data { int a, b; //默认为私有的,外部不能直接访问

public://公有的,外部可以直接访问

void set(int i, int j,int k,int l,int m,int n) { a=i,b=j,c=k,d=l,e=m,f=n;}

protected://保护的,外部不能直接访问,派生类可以访问

int c, d;

private://私有的,外部不能直接访问,派生类也不可以访问

int e, f;

};

::是作用域限定符。如果在作用域限定 符的前面没有类名,或者函数前面既无类名又无作用域限定符,例 如:

::set(10) 或 set(10)

则表示set函数不属于任何类,这个函数不是成员函数,而是全局 的普通函数。

此时的(::)不是类作用域限定符的含义,而是命名 空间域限定符的含义。

class Data { //Data类定义

public: void set(int d); //成员函数原型声明

int get() { //成员函数定义

return data;

} //get函数定义结束

private:

int data; //数据成员

}; //Data类定义结束

void Data::set(int d) //成员函数的外部定义,使用 Data:: 限定

{ data=d; //访问类的数据成员

}

void set(int d) //全局普通函数

{

… //函数体 }

class Time { //Time类

int h,m,s; //数据成员

void settime(int a,int b,int c)

{ h=a,m=b,s=c;} //成员函数

};

►sizeof(Time)的值是12。显然,Time类的存储空间长度只取决于数 据成员h、m、s所占的空间,而与成员函数settime无关。

C++把成 员函数的代码存储在对象空间之外的地方。

类不能具有自身类型的数据成员。然而,只要类名一经出现就可以 认为该类己声明。因此,类的数据成员可以是指向自身类型的指针 或引用。

class Point; //Point类声明,非Point类定义,因为没有类体

class Line { Point a; //错误,不能使用仅有类声明而没有类定义的类定义数据对象

Point *pp, &rp; //正确,只有类声明,即可用它定义该类的指针或引用

Line b; //错误,类不能具有自身类型的数据成员

Line *pl, &rl; //正确,类可以有指向自身类型的指针或引用的数据成员

};

►构造函数就是用来用来在创建对象时初始化对象, 为对象数据成 员赋初始值。一般为公有的

class Cuboid { //Cuboid类表示长方体

public:

Cuboid(int l,int h, int d); //构造函数

int volumn() { return length*height*depth; }; //计算体积

private:

    int length,height,depth; //长、高、深

}

Cuboid::Cuboid(int l,int h,int d) :length(l),heght(h),depth(d) //带构造函数初始化列表的构造函数

{

    cout<<"Cuboid: "<<"L="<<l<<",H="<<h<<",D="<<d<<endl;

}

►(1)必须在类的内部指定构造函数的默认参数,不能在类外部指 定默认参数。

构造函数重载:

class Point { //Point类表示平面上的点

public:

Point() { x=y=0; } //无参数的构造函数

Point(int a, int b) : x(a), y(b) { } //有参数的构造函数

}

复制构造函数:

class Point { //Point类

public: Point() : x(0), y(0) { } //默认构造函数

Point(const Point& r) : x(r.x), y(r.y) { } //复制构造函数

Point(int a,int b) : x(a), y(b) { } //带参数构造函数

private: int x,y;

};

►复制构造函数的功能是利用一个已知的对象来初始化一个被创建的 同类的对象。

Point pt1(10,20);

Point pt2=pt1; //复制初始化

Point pt3(pt1); //直接初始化

►析构函数不返回任何值,没有返回类型,也没有函数参数。由于没 有函数参数,因此它不能被重载。

换言之,一个类可以有多个构造 函数,但是只能有一个析构函数。

    public:

       point (int a,int b) : x(a),y(b){

       }

       //析构函数的作用并不仅限于释放资源方面

       ~point(){//用来执行对象即将被撤销之前程序员所期待的任何操作

           cout << "调用析构函数" << endl;

       } //都是最后执行的

例如:

new的对象被delete,就是调用了析构

先构造的后析构,后构造的先析构。

对象数组:

    public:

       int a;

       point(int l):a(l){

       }

       ~point(){

           cout << "执行" << endl;

       } 

 };

 int main()

 {

    //(2)如果对象数组所属类有带参数的构造函数时,可用初始化列 表按顺序调用构造函数,使用复制初始化来初始化每个数组元素。

//(3)如果对象数组所属类有单个参数的构造函数时,定义数组时 可以直接在初值列表中提供实参。

    point pin[5] = {1,2,3,4,5};

    point m(2);  

    int *p = &m.a;

     //数据成员指针►指向a的指针的完全类型是“指向int类型的point类成员的 指针”,即:

    int point :: *pt = &point :: a;

    cout << m.*pt << endl;

成员函数指针:

    ►定义成员函数的指针时必须确保在三个方面与它所指函数的类型相 匹配:

►①函数形参的类型和数目,包括成员是否为const。②返回类型。 ►③所属类的类型。 ►定义的一般形式为:

返回类型 (类名::*指针变量名)(形式参数列表)=成员地址初值;

返回类型 (类名::*指针变量名)(形式参数列表) const =成员地址初值;

Data d, *p=&d; //指向对象d的指针

int Data::*pt = &Data::top; //pt为指向数据成员top的指针

int k = d.top; //对象成员引用,直接访问对象,直接访问成员,与下面等价

k = d.*pt; //对象成员指针引用,直接访问对象,间接访问成员

k = p->top; //指针成员引用,间接访问对象,直接访问成员,与下面等价

k = p->*pt; //指针成员指针引用,间接访问对象,间接访问成员

char (Data::*pmf)(int,int) const; //pmf为成员函数指针

pmf = &Data::get; //指向有两个参数的get函数

char c1 = d.get(0,0); //对象直接调用成员函数,与下面等价

char c2 = (d.*pmf)(0,0); //对象通过成员函数指针间接调用成员函数

char c3 = (p->*pmf)(0,0); //指针间接引用对象通过成员函数指针间接调用成员函数

静态成员、类中定义的类型成员需要直接通过类作用域运算 符“::”来访问。

class Data { public: enum COLORS {RED,GREEN,BLUE,BLACK,WHITE}; //声明枚举类型

COLORS getcolor();

}; COLORS c1; //错误,COLORS只能在类作用域中

Data::COLORS cc; //正确,Data:: 限定COLORS在类作用域中

//const point d1; //定义常对象

    point const p(1);

    point n,b;

    point *const m = &n;//这是指向对象的常指针,之后不能更改指向方向,比如:m = &b;

    const point *k;//指向常对象的指针变量 ,指向的对象不能通过指针改变

//可以将数据成员声明为mutable(可变的)来修改它的值,mutable int data; //可变的数据成员

p.a = 10; //错误,常对象数据成员data为const,不能成为左值

p.show(); //错误,不能调用常对象中非const型成员函数

►关于静态数据成员的说明:

►(1)通常,非静态数据成员存在于类类型的每个对象中,静态数 据成员则独立于该类的任何对象,

在所有对象之外单独开辟空间存 储。在为对象所分配的空间中不包括静态数据成员所占的空间。

►(2)如果只声明了类而未定义对象,则类的非静态数据成员是不 占存储空间的,只有在定义对象时,才为对象的数据成员分配空间。

但是只要在类中定义了静态数据成员,即使不定义任何对象,也为 静态数据成员分配空间,它可以在尚未建立对象时就被引用。

    private:

       int x;

    public:

       static int con;

       point(){

       }

       static void test(){

           //x += 1;错误,静态的不能调用非静态的

       }

};

int point::con = 10;//静态成员必须这么调用

int main()

{  

    point :: test();//静态函数必须这么调用 ,非静态可以调静态的,反过来不行

友元可以是普通的函数,或已定义的其他类的成员函数

关于友元类的说明:

►(1)友元的关系是单向的而不是双向的。如果声明了类B是类A的 友元类,不等于类A是类B的友元类,

类A中的成员函数不能访问类 B中的私有数据。

►(2)友元的关系不能传递或继承,如果类B是类A的友元类,类C 是类B的友元类,不等于类C是类A的友元类。

如果想让类C是类A的 友元类,必须显式地在类A中另外声明。

    private:

       int c;

       void friend set(point &r);//友元

};

//友元可以是普通的函数,或已定义的其他类的成员函数

void point :: se()

{

    cout << c << endl;

}

void set(point &r)

{

    cout << r.c << endl;

}

class point

{

    public:

       int a;

       point(int b):c(b){

       }

       void se();

    private:

       int c;

       friend class mon;//友元类

};

//友元类B中 的所有成员函数都是A类的友元函数,可以访问A类中的所有成员。反过来不行

class mon

{

    public:

       int aa;

       mon(int b) :aa(b){

       }

       void set(point &r)

       {

           cout << r.c << endl;

       }

 }

 类派生列表可以指定多个基类,中间用逗号(,)间隔,基类名必 须是已定义的类的名字。

►访问标号表示继承方式,可以是public(公有继承)、protected (保护继承)或private(私有继承),

继承方式决定了对继承成员 的访问权限。如果未给出访问标号则默认为private(私有继承)。

►派生类的成员列表描述的是派生类自己新增加的数据成员和成员函 数。

►(4)友元关系不能继承。一方面,基类的友元对派生类的成员没 有特殊访问权限。

另一方面,如果基类被授予友元关系,则只有基 类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。

(5)如果基类定义了静态成员,则整个继承层次中只有一个这样 的成员。无论从基类派生出多少个派生类,每个静态成员只有一个 实例。

►(6)静态成员遵循常规访问控制:如果静态成员在基类中为私有 的,则派生类不能访问它。

如果该静态成员在基类是公有的,则基 类可以访问它,派生类也可以访问它。

class point//基类

{

    public:

       int a;

       point (int b):c(b){

       }

       void print()

       {

           cout << "111:" << c << endl;

        }

        protected:

           int min = 10;

        private:

           int c;

};

class mon : public point//派生类

{

    public:

       mon(int y,int x):point(y),m(x){//派生类的构造函数

       }

       void print()

       {

           cout << "kkkk" << endl;

       }

       void set()

       {

           cout << min << endl;

       }

    private:

       int m;

};

int main()

{

    mon p(9,2);

    point q(3);

    q.print();

    p.point::print();

    p.print();

    p.set();

    return 0;

►只有基类类成员及其友元可以访问基类的private部分,派生类不能 访问基类的私有成员。

类的protected部分仍然不能被类用户访问,但可以被派生类 访问。

►赋值兼容规则中所指的替代包括以下的情况:

►①派生类的对象可以赋值给基类对象;

►②派生类的对象可以初始化基类的引用;

►③派生类对象的地址可以赋给指向基类的指针。

►例:

class Base { }; //基类

class Derive : public Base { }; //公有派生类

Base b,*pb; //定义基类对象、指针

Derive d; //定义派生类对象

►这时,支持下面三种操作:

b=d; //派生类对象赋值给基类,复制基类继承部分

Base &rb=d; //基类引用到派生类对象

pb=&d; //基类指针指向派生类对象

派生类的析构函数:

class A{ public:

A(){cout<<"A constructor"<<endl;}

~A(){cout<<"A destructor"<<endl;}

};

class B: public A{ public:

B(){cout<<"B constructor"<<endl;}

~B(){cout<<"B destructor"<<endl;}

};

class C: public B{ public:

C(){cout<<"C constructor"<<endl;}

~C(){cout<<"C destructor"<<endl;}

};

多重继承和派生:

class A//基类

{

    private:

       int a;

    public:

       A(int b):a(b){

       }

    void fun(){

       cout << "111" << endl;

    }  

 };

 class B//基类

 {

    private:

       int a;

    public:

       B(int b):a(b){

        }

       void fun()

        {

           cout << "222" << endl;

               }    

 };

 class C :public B , public A//多重继承派生

 {

    private:

       int a;

    public:

       C(int a,int b,int c):B(a),A(b),a(c){//多重继承派生构造

        }

        void fun()

        {

           cout << "333" << endl;

           B::fun();//调用B中的fun

               }    

 };

 int main()

 {

    C a(1,2,3);

    a.A::fun();//A中的fun

    a.B::fun();//B中的fun

    a.fun();//C中的fun

关于虚基类的说明: ► (1) 一个类可以在一个类族中既被用作虚基类,也被用作非虚基 类。

►(2)派生类的构造函数的成员初始化列表中必须列出对虚基类构 造函数的调用;如果未列出,则表示使用该虚基类的默认构造函数。

►(3)在一个成员初始化列表中同时出现对虚基类和非虚基类构造 函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

class A//虚基类

{

    private:

       int a;

    public:

       A(int b):a(b){

       }

       void fun(){

           cout << "111" << endl;

       }  

 };

 class B : virtual public A //派生虚基类

 {

    public:

       B(int b):A(b){

        }    

 };

  class D : virtual public A //派生虚基类

 {

    public:

       D(int b):A(b){

        }    

 };

 //在一个成员初始化列表中同时出现对虚基类和非虚基类构造 函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

 class C :public B , public D //派生基类

 {

    public:

       C(int a):A(a),B(a),D(a){

        } 

 };

 int main()

 {

    C a(1);

    a.A::fun();//A中的fun

►当调用虚函数时,先通过vptr找到虚函数表,然后再找出虚函数的 真正地址,再调用它。

►派生类能继承基类的虚函数表,而且只要是和基类同名(参数也相 同)的成员函数,无论是否使用virtual声明,它们都自动成为虚函 数。如果派生类没有改写继承基类的虚函数,则函数指针调用基类 的虚函数。

如果派生类改写了基类的虚函数,编译器将重新为派生 类的虚函数建立地址,函数指针会调用改写以后的虚函数。

class Base {public: virtual void print() { cout<<"Base"<<endl;}//虚函数 };

class Derived: public Base { public: void print() { cout<<"Derived"<<endl; } //虚函数 };

void display(Base *p)

    { p->print(); }

  int main() {

  Derived d; Base b;

display(&d); //派生类对象,输出“Derived”

display(&b); //基类对象,输出“Base”

►虚函数的调用规则是:根据当前对象,优先调用对象本身的虚成员 函数。这和名字支配规律类似,不过虚函数是动态联编的,

是在运 行时(通过虚函数表中的函数地址)“间接”调用实际上欲联编的 函数。

►2. 虚函数实现多态的条件 ►①类之间的继承关系满足赋值兼容性规则; ►②改写了同名的虚函数; ►③根据赋值兼容性规则使用指针(或引用)。

►满足前两条并不一定产生动态联编,必须同时满足第3条才能保证 实现动态联编。

►1. 纯虚函数 ►在许多情况下,不能在基类中为虚函数给出一个有意义的定义,这 时可以将它说明为纯虚函数(pure virtual function),将具体定义 留给派生类去做。纯虚函数的定义形式为:

virtual 返回类型 函数名(形式参数列表)=0;

►即在虚函数的原型声明后加上“=0”,表示纯虚函数根本就没有函 数体。

►重载运算符的规则 ►(1)C++绝大部分的运算符可以重载,不能重载的运算符有: . .* :: ?: sizeof

►(2)不能改变运算符的优先级、结合型和运算对象数目。

►(3)运算符重载函数不能使用默认参数。

►(4)重载运算符必须具有一个类对象(或类对象的引用)的参数, 不能全部是C++的内置数据类型。

►(5)一般若运算结果作为左值则返回类型为引用类型;若运算结 果要作为右值,则返回对象。

►(6)重载运算符的功能应该与原来的功能一致。

►就像可以定义函数模板一样,也可以定义类模板。其定义的一般形 式为:

template <模板形参表> class 类模板名 {//类体 成员列表

};

►类模板必须以关键字template开头,后接模板形参表。模板形参表 是用一对尖括号< >括住的一个或多个模板形参的列表,不允许为 空,形参之间以逗号分隔。其一般形式为:

<class 类型参数1, class 类型参数2, ...... >

模板形参还可以设置默认值。例如:

template <class T=char , int N=10> //类模板定义 class Sequence {…};

►则对象定义时可以有以下形式:

Sequence <> m; //使用默认类型char和使用默认值10 Sequence <double> n; //提供类型和使用默认值10 Sequence <int,100> k; //提供类型和常量表达式

template <class T>//类模板定义

class point

{

    public:

       point():x(0),y(0){

       }

       void set(const T a);

    private:

       int x,y;

 };

 template <class T>

 void point<T> :: set(const T a)

 {

    cout << a << endl;

  }

 int main()

 {

    point <int> a;//类模板的调用

    a.set(9);

命名空间的定义:

namespace { int i; } void f(){ i++; }

namespace A{ namespace { int i,j;

}

void g(){ i++; }

}

using namespace A;

void h(){ A::i++; j++;

}

使用std限定的一个好处就是对每个std成员做限定,例如: std::cout<<"hello,world."<<std::endl;

►这样做虽然不方便,但是最大程度的避免了与标准命名空间的名字 冲突。

►也可以用如下方法:

using std::cin; using std::cout; using std::endl;

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浮若于心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值