1.C++的多态问题
定义:指相同的对象收到不同的消息或者不同对象收到相同消息时产生的不同动作。
多态分成:静态多态(编译时多态—函数重载和模板),
动态多态(运行时多态—虚函数virtual允许子类重写这个函数(继承和派生))
①模板:
输入int a和b;double a和b,返还的c是不同的类型。
template <typename T>
T add(T a, T b)
{
t c = a+b;
return c;
}
②重载:
输入int就自动进入第一个,输入float就自动进入第二个。
int add(int a,int b)
int add(float a,float b)
③虚函数(继承和派生)
虚函数就是在原函数之前加上一个virtual使其变成虚函数。指向基类的指针在造作它的多态对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。
class A {
public:
void f1()
{
cout<< "A::f1()" << endl;
}
virtualvoid f2()
{
cout<< "A::f2()" << endl;
}
};
class B :public A
{
public:
void f1()
{
cout<< "B::f1()" << endl;
}
voidf2() //因为是继承过来的,所以自动转换为了virtual
{
cout<< "B::f2()" << endl;
}
};
int main()
{
A* p = newB(); //注意这里指向的是B
B* q = newB();
p->f1(); //调用A::f1()
p->f2(); //调用B::f2()体现多态性
q->f1(); //调用B::f1()
q->f2(); //调用B::f2()
system("pause");
return 0;
}
2.内联函数
内联函数如下:
inline int max(int a, int b)
{
return a > b ? a : b;
}
则调用:cout<<max(a, b)<<endl
在编译时展开为:cout<<(a > b ? a : b)<<endl;
调用函数比秋姐等价表达式要慢得多,因此使用内联函数的效率会更高。
它可以理解成define的升级版,它在编译时可以替换掉指定位置的函数。define是不能编译的就只是简单的替换,而是用内联函数是可以进行编译的。并且编译器可以通过上下文相关的内容,对代码进行进一步的优化。
PS:析构函数可以是内联函数。
指针和引用都是间接使用一个对象的方法。
指针是个对象,而引用不是。
引用必须设初值,并且今后不能改变。
使用引用之前不必检查。
操作符重载的时候,有的时候语法要求只能返回引用。
4.析构函数和构造函数
①构造函数:
构造函数就是与class的名称一样的,写在类里边的同名函数,一般用来做初始化。构造函数不能是虚函数,是因为创建一个对象,就必须要准确的知道这个对象的类型,所以构造函数不能以为虚函数。一个class类里可以有多个构造函数,但是所有的构造函数都没有返回值(可以设定为有返回值的构造函数)。
class Line {
public:
void setLength( double len );
double getLength( void );
Line(double len); // 这是构造函数
private:
double length;
};
②析构函数:
当一个类的对象离开作用域时,析构函数则会被调用,主要用来删除一些数据,free一些内存。析构函数与class的名称一样,但是需要在函数前增加一个“~”标识符号。它主要用来清理,为了保证多态性,所以析构函数必须为虚函数。
析构函数没有参数,也没有返回值,而且不能重载,因此一个类中只能有一个析构函数。当派生类的对象被撤销时,肯定会依次调用其基类的析构函数。
class Line {
public:
void setLength( double len );
double getLength( void );
Line(); // 这是构造函数声明
~Line(); // 这是析构函数声明
private:
double length;
};
5.静态库和动态库
库,是别人已经写好的,成熟的可以直接调用的代码,在Windows下的静态库是.lib文件,动态库是.dll文件。Linux下.so为静态库,.a为动态库。
①静态库是链接的阶段,会将汇编生成的目标文件和引用到的库一起链接打包到可执行文件中,这种方式也成为静态链接。
优点:静态库对函数库的链接是放在编译时期完成的,程序运行时与函数库再无瓜葛,移植方便,这种方式使其运行的速度比动态链接的快。
缺点:但是会吧所有的静态库都编译进去,这样会浪费很多内存和资源。
在使用VS的时候,一般都是项目属性->配置属性->常规里边找附加依赖项添加静态库。
②动态库在程序编译时并不会被连接到代码中,而是在运行的时候才开始被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份共享库即可供所有的程序使用。
优点:它有效的解决了静态库更新之后要所有程序重新编译的缺点,使程序升级变得很简单。.dll与.exe文件相互独立,可以更换.dll文件不会给.exe文件带来任何影响。
缺点:移植性差,容易造成.dll文件缺失,返回错误信息等等。
6.友元friend
类具有封装和隐藏信息的特性,只有类的成员函数才能访问类的私有成员,程序中的其他函数都是无法访问私有成员的。有的时候需要调用内部的函数,又不想破坏封装的特性,发明了友元。友元在class中对一个函数进行声明,之后在外边写一个简单的函数,使其可以调用外边的函数。提高了运行的效率,但从一定程度上破坏了封装性和隐藏性。
用友元做简单的加和:
Class Point{
private:
float x;
float y;
public:
point(floata=0.0f,floatb=0.0f):x(a,b){};
friendfloat distance(Point &left,Point &right)
}
float distance(Point &left,Point &right){
return(left+right);
}
7.纯虚函数和虚函数
虚函数必须得在基类中进行声明,而纯虚函数并不需要。
C++中包含纯虚函数的类,被称为是“抽象类”。抽象类不能使用new出对象,只有实现了这个纯虚函数的子类才能new出对象。
C++中的纯虚函数更像是“只提供申明,没有实现”,是对子类的约束,是“接口继承”。
C++中的纯虚函数也是一种“运行时多态”。
8.虚函数是非常有效的,是否可以把每个函数都声明为虚函数?
所有的函数定义为虚函数,在编译的时候不会报错,但是实际应用中不能这么写。
使用虚函数都是有代价的,每个虚函数的对象都必须维护一个v表,因此使用虚函数的时候都会产生一个开销,都使用虚函数会导致开销过大。如果是一个很小的类,且不想派生其他的类,没必要使用虚函数。
9.宏定义define、枚举enum、typedef、const:
① 宏定义:
#define a 1
#define zla "ABCD"
define是预处理指令,在编译预处理时进行替换,不检查正确性,比如上边的zla*33编译之前是不会出错的,会在编译的时候出错。
优点:全局内可以定义多种类型的值,整形、浮点型、字符串等;
缺点:全局范围有效容易造成冲突,多个相关值一起定义比较散乱;
② 枚举:
enum week
{
MON,
TUES,
WED,
THUR,
FRI,
SAT,
SUN,
};
优点:遵循范围规则,不容易与其他的定义发生冲突,多个关键值一组,比较清晰;
缺点:只能为整型值,并且赋值的顺序是系统默认的;
③ typedef
相当于关键字的别名,他不是简单的替换,新的名称有一定的封装性,是语言编译过程的一部分,并不实际分配内存空间。
Typedef int ZLA
ZLA a=3 相当于 int a=3
举例子:
Typedef (int*)Zla1
#define Zla2int*
Zla1 a,b; //相当于 int *a,*b;
Zla2 a,b; //相当于 int *a,b;b并不是指针
④ const
const常亮在定义的时候有数据类型,编译器可以进行安全检查,但是define则不能进行安全检查。有一些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。尽量用const代替define。
const int a = 0;
int main()
{
int a = 2; //可以有重复
cout << a <<endl;
return 0;
}
10.Sizeof
当参数分别如下时,sizeof返回的值表示的含义如下:
数组——编译时分配的数组空间大小;
指针——存储该指针所用的空间大小(存储该指针的地址的长度,是长整型,一般为4);
类型——该类型所占的空间大小;空类型sizeof为1.
对象——对象的实际占用空间大小;
函数——函数的返回类型所占的空间大小。函数的返回类型不能是void。
当数组作为参数是,数组就退化成了指针