一:内联函数
函数调用也会带来降低效率的问题,因为调用函数实际上将程序执行顺序转移到函数所存放在内存中某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方。这种转移操作要求在转去前要保护现场并记忆执行的地址,转回后先要恢复现场,并按原来保存地址继续执行。
因此,函数调用要有一定的时间和空间方面的开销,于是将影响其效率。特别是对于一些函数体代码不是很大,但又频繁地被调用的函数来讲,解决其效率问题更为重要。引入内联函数实际上就是为了解决这一问题。
优点:可以加快代码的执行速度,当程序中调用内联函数时,该函数直接嵌入到每个调用语句处,每次函数调用时都用相对应的一段代码代替。可见它是以目标代码的增加为代价来换取时间的节省
主要解决功能相对简单、规模不大但使用相当频繁的程序运行效率问题。
使用内联函数时,遵守以下规则:
1. 内联函数体内不能包含任何静态变量,不能使用循环语句、switch;不能递归。
2.内联函数的定义必须出现在第一次被调用之前。
3.如果函数返回类型为void,则不能有return 语句。
二:指针
通过指针引用数组元素
int a[10];
int *p;
p++是合法的,而a++是错误的。a是数组名,它是数组的首地址,是常量;
指向函数的指针变量:存放函数入口地址,指向的是程序代码存储区。
1、函数调用可以通过函数名调用,也可以通过指向函数的指针变量调用。
2、(*p) ( )表示定义一个指向函数的指针变量,在程序中把哪个函数的地址赋给它,它就指向哪一个函数。
3、给函数指针变量赋值时,只需给出函数名而不必给出参数。 p = max
4、用函数指针变量调用函数时,只需将(*p)代替函数名,在(*p)之后的括弧中根据需要写实参。 c=(*p)(a,b)
5、对指向函数的指针变量不能运算
const pointer
一个指针涉及到两个变量,一个是指针本身,另一个是指向的变量
1.指向常量的指针
const放在指针类型前,在程序中不能通过指针来间接修改指针所指向的内存空间的值,但可以改变指向的空间
int a = 10;
const int b = 20;
const int* pa = &a;
const int* pb = &b;
a = 100; // ok
b = 200; // error
*pa = 100; // error
*pb = 200; // error
pb = &a; // ok
pa = &b; // ok
2.指针常量
const放在”*”和指针名之间,不能改地址能改所指向的值。
int b =28;
int* const pb = &b;
*pb = 100; // ok
pb++; // error
在定义指针常量时,必须将其初始化。
3.指向常量的指针常量
const在上述两个地方都加。既不允许修改指针值,也不允许修改指针变量所指向的值
const int a = 10;
const int * const pa = &a;
a = 100; // error
*pa = 100; // error
pa ++; // error
三:引用
标题
引用与指针的区别
引用是 C++ 语言引进的概念,在 C 语言中没有。
1)、指针是变量,引用是别名
引用是别名,引用本身没有地址。
2)、指针可作数组元素、引用不可
例: int *pa[5];//指针数组
int a[5];
int &rea[5]=a;//不可
…
3)、可以有空指针,不可有空引用
例: int *p=null;//合法
int &re=null;//无意义
四:类(class)
私有(private)的数据和函数,只允许本类的成员函数访问或调用;
保护(protected:)的数据和函数,允许本类和本类派生类的成员函数访问或调用;
公有(public:)的数据和函数,允许本类和其它类的函数访问或调用。
静态数据成员:
1.类的静态数据成员为该类的所有对象所共享。
2. 必须在类外文件作用域中的某个地方对静态数据成员赋初值(因为构造函数多次被调用,而静态数据成员只初始化1次):
<类型> <类名>::<静态数据成员> = <初值>
Const 成员函数中确保不会修改任何本类对象的数据成员。
class constfun
{
private:
int a;
public:
void nonconstFunc( )
{
a=18; //ok
}
void Func( ) const
{
a=18 //error
}
}
this指针:
this指针是指向对象的指针,隐含在类的成员函数中,用来指向成员函数所属类的正在被操作对象。this指针可以看作是类自身的一个引用。
构造函数的调用顺序
对于构造函数,先执行基类的,再执行对象成员的,最后执行派生类的。
对于析构函数,先执行派生类的,再执行对象成员的,最后执行基类的。
导出类构造函数和析构函数的构建
基类的构造函数和析构函数不能被派生类继承。
如果基类没有定义构造函数,派生类也可以不定义构造函数,全都采用缺省的构造函数,此时,派生类新增成员的初始化工作可用其他公有函数来完成。
如果基类定义了带有形参表的构造函数,派生类就必须定义构造函数,提供一个将参数传递给基类构造函数的途径,以便保证在基类进行初始化时能获得必需的数据。
如果派生类的基类也是派生类,则每个派生类只需负责其直接基类的构造,不负责自己的间接基类的构造。
派生类是否要定义析构函数与所属的基类无关,如果派生类对象在撤销时需要做清理善后工作,就需要定义新的析构函数。
五:多重继承
解决二义性问题
解决方法一:用类名来限定(主要解决方法) 为避免二义性,可在调用时加上基类的名称,如 A::print() 或 B::print() 。
解决方法二:同名覆盖在C 中声明一个同名成员函数print(),f()再根据需要调用 A:: print() 或 B:: print()
解决方法三:使用虚函数
面向对象设计的三大机制: 数据封装、继承、多态。
继承:研究的是类与类之间的层次关系。
多态性:指不同的对象接收到相同的消息时产生不同的响应动作,即对相同的函数名,却执行不同的函数体。
函数重载和运算符重载实现类的一种多态性。
静态联编和动态联编
联编(binding):是将函数调用与相应的函数体代码彼此关联的过程。
静态联编(static binding):如果联编过程在程序开始运行前的编译阶段完成。
例如:重载函数:
void fun(int a,int b)
void fun(float x,float y)
void fun(char c)
函数名字相同,但各自参数不同,编译器能根据函数参数的类型和个数自动选择相应的函数体编译。
动态联编(dynamic binding)
在程序运行时进行的联编方式。
例如:虚函数
C++中的虚函数,由于其函数名、返回值、函数参数完全相同,但函数体不同,因此编译阶段无法确定函数调用与哪个函数体关联,只能由系统在程序运行时确定。
六:虚函数
虚函数(virtual function)----运行时多态
在定义某一基类(或其派生类)时,若将其中的某一函数成员的属性说明为virtual,则称该函数为虚函数。
若基类中某函数被说明为虚函数,则意味着其派生类中也要用到与该函数同名、同参数表、同返回类型、但函数体不同。
虚函数存在继承环境中。
虚函数成员的定义语法:
virtual 函数类型 函数名(形参表)
{
函数体
}
程序举例
class BaseClass
{
public:
virtual void show() {cout<<"Base class"<<endl;}
//如果不加关键字virtual,运行的结果都是"Base class";
};
class Derived1:public BaseClass
{
public:
void show() {cout<<"Derived class1"<<endl;}
};
class Derived2:public BaseClass
{
public:
void show() {cout<<"Derived class2"<<endl;}
};
int main(){
BaseClass obj;
BaseClass *p;
Derived1 obj1;
Derived2 obj2;
p=&obj;
p->show();
p=&obj1;
p->show();
p=&obj2;
p->show();
return 0;
}
通过虚函数,达到了用基类指针访问派生类对象成员函数的目的,这样,只要声明了基类指针,就可以使不同的派生类对象产生不同的函数调用,实现了程序的运行时多态。
运行多态应该使用虚函数,并通过指针、引用或者成员函数调用虚函数
纯虚函数和抽象类
纯虚函数(pure virtual function):
在基类中声明虚拟函数而不给出具体的定义,把它的定义放在各个导出类中,此种函数为纯虚函数
通过基类指针或引用可以调用所有派生类的虚函数。
抽象类:(abstract class)
声明了虚函数的类,基类只用于继承,仅作为一个接口,具体的功能则在派生类中实现。
注意:从抽象类可以派生出具体的或抽象类,但不能从具体类派生出抽象类。
虚基类
虚基类的引入
用于有共同基类的场合
声明
以virtual修饰说明基类例:class B1:virtual public B
作用
主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义性问题.
为最远的派生类提供唯一的基类成员,而不重复产生多次拷贝
类模板
C++中实现多态的另一种方法是类模板
类模板可以使用户为类定义一种模式,使得类中的一些数据成员和成员函数的参数,以及成员函数的返回值能够娶任意类型。
#include <iostream.h>
template <class T> class TestClass {
public:
T buffer[10];
//T类型的数据成员buffer数组大小固定为10 (灵活性差!)
T getData(int j); //获取T类型buffer(数组)的第j个分量
};
template <class T>
T TestClass<T>::getData(int j) {
return *(buffer+j);
};
void main() {
TestClass<char> ClassInstA;
//char取代T,从而实例化为一个具体的类
char cArr[6]="abcde";
for(int i=0; i<5; i++)
ClassInstA.buffer[i]=cArr[i];
for(i=0; i<5; i++) {
char res=ClassInstA.getData(i);
cout<<res<<" ";
}
cout<<endl;
程序执行后的显示结果如下:
a b c d e
2.1 13.2 24.3 35.4 46.5 57.6
既使用类型参数又使用 普通参数的类模板示例
#include <iostream.h>
#include "string.h"
template <class T, int i> class TestClass {
public:
T buffer[i];
//T类型的buffer,其大小随普通形参i的值变化(灵活性大!)
T getData(int j);
};
template <class T, int i>
T TestClass<T,i>::getData(int j) {
return *(buffer+j);
};
TestClass<double, 6> ClassInstF;
double fArr[6]={12.1, 23.2, 34.3, 45.4, 56.5, 67.6};
for(i=0; i<6; i++)
ClassInstF.buffer[i]=fArr[i]-10;
for(i=0; i<6; i++) {
double res=ClassInstF.getData(i);
cout<<res<<" ";
}
cout<<endl;
}
程序执行后的显示结果如下:
a b c d e
2.1 13.2 24.3 35.4 46.5 57.6
版权声明:本文为博主原创文章,未经博主允许不得转载。