【C/C++】C++基础知识查漏补缺

来源:互联网 发布:数据库的规范化设计 编辑:程序博客网 时间:2024/04/30 14:37

1、C++多态性的概念(转自C语言中文网

多态性是面向对象程序设计的一个重要特征。如果一种语言只支持类而不支持多态,是不能被称为面向对象语言的,只能说是基于对象的,利用多态性可以设计和实现一个易于扩展的系统。

在C++程序设计中,多态性是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数。也就是说,每个对象可以用自己的方式去响应这个函数调用行为。

除了类里面的多态,其实,函数的重载、运算符重载都是多态现象。

多态性分为两类:静态多态性动态多态性。函数重载和运算符重载实现的多态性属于静态多态性,在程序编译时系统就能决定调用的是哪个函数,因此静态多态性又称编译时的多态性。动态多态性是在程序运行过程中才确定,动态多态性是通过虚函数实现的。



2、 动态绑定与静态绑定(转自http://blog.csdn.net/chgaowei/article/details/6427731(常高伟的专栏《深入理解C++的动态绑定和静态绑定》))

为了支持c++的多态性,才用了动态绑定和静态绑定。理解他们的区别有助于更好的理解多态性,以及在编程的过程中避免犯错误。

需要理解四个名词:

1、对象的静态类型:对象在声明时采用的类型。是在编译期确定的。

2、对象的动态类型:目前所指对象的类型。是在运行期决定的。对象的动态类型可以更改,但是静态类型无法更改。

关于对象的静态类型和动态类型,看一个示例:

class B  {  }  class C : public B  {  }  class D : public B  {  }  D* pD = new D();//pD的静态类型是它声明的类型D*,动态类型也是D*  B* pB = pD;//pB的静态类型是它声明的类型B*,动态类型是pB所指向的对象pD的类型D*  C* pC = new C();  pB = pC;//pB的动态类型是可以更改的,现在它的动态类型是C*  

3、静态绑定:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期。

4、动态绑定:绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期。

class B  {      void DoSomething();      virtual void vfun();  }  class C : public B  {      void DoSomething();//首先说明一下,这个子类重新定义了父类的no-virtual函数,这是一个不好的设计,会导致名称遮掩;这里只是为了说明动态绑定和静态绑定才这样使用。      virtual void vfun();  }  class D : public B  {      void DoSomething();      virtual void vfun();  }  D* pD = new D();  B* pB = pD;  

让我们看一下,pD->DoSomething()和pB->DoSomething()调用的是同一个函数吗?

不是的,虽然pD和pB都指向同一个对象。因为函数 DoSomething() 是一个 no-virtual 函数,它是静态绑定的,也就是编译器会在编

根据对象的静态类型来选择函数。pD的静态类型是D*,那么编译器在处理 pD->DoSomething() 的时候会将它指向

D::DoSomething() 。 同理,pB的静态类型是B*,那 pB->DoSomething() 调用的就是 B::DoSomething() 。


让我们再来看一下,pD->vfun()和pB->vfun()调用的是同一个函数吗?

是的。因为 vfun 是一个虚函数,它是动态绑定的,也就是说它绑定的是对象的动态类型,pB和pD虽然静态类型不同,但是他们同

指向一个对象,他们的动态类型是相同的,都是D*,所以,他们的调用的是同一个函数:D::vfun()。


上面都是针对对象指针的情况,对于引用(reference)的情况同样适用。

指针和引用的动态类型和静态类型可能会不一致,但是对象的动态类型和静态类型是一致的。

D D;
D.DoSomething() 和 D.vfun() 永远调用的都是 D::DoSomething() 和 D::vfun() 。

至于哪些是动态绑定,哪些是静态绑定,有句话总结的非常好:只有虚函数才使用的是动态绑定,其他的全部是静态绑定。




3、C++四种类型转换

1)const_cast

去掉常量指针及常量引用的const或volatile属性,然后我们就可以使用该指针或引用修改其值。

套用 overstack 上的回答:

You are not allowed to const_cast variables that are actually const. This results in undefined behavior. const_cast isused to remove the const-ness from references and pointers that ultimately refer to something that is not const.


举个例子:

int i = 0;const int& ref = i;const int* ptr = &i;const_cast<int&>(ref) = 3;*const_cast<int*>(ptr) = 3;
这样是正确的使用方法。



const int i = 0;const int& ref = i;const int* ptr = &i;const_cast<int&>(ref) = 3;*const_cast<int*>(ptr) = 3;
这个是错误的使用方法。


重要的事情说三遍:

Modifying an object that is const is undefined behavior!

Modifying an object that is const is undefined behavior!

Modifying an object that is const is undefined behavior!

Don't do this!!!




2)static_cast

类似于C风格的强制转换。无条件转换,静态类型转换。用于:

_1、基类和派生类之间转换,其中派生类指针转换成基类指针是安全的,但基类指针转换成派生类指针是不安全的(基类和子类之间的动态类型转换建议用dynamic_cast)  ;

_2、基本数据类型转换,如 int, char, float 等。但需注意不能进行无关类型(如非基类和派生类)指针之间的转换。

_3、把 NULL 指针转换成目标类型的 NULL 指针;

_4、把任何类型的表达式转换成void类型;

_5、static_cast不能去掉类型的const、volitale属性(用const_cast)。


3)dynamic_cast

有条件转换,动态类型转换,运行时类型安全检查(转换失败返回NULL):
_1、安全的父类指针和子类指针之间转换;
_2、必须要有虚函数;
_3、相同基类不同派生类之间的交叉转换,结果是NULL。

举例:

class base{public:int a;base(int _a):a(_a){}virtual void display() const {cout << a << endl;}};class derive : public base{public:int b;derive(int _a, int _b):base(_a),b(_b){}virtual void display() const {cout << a << '\t' << b << endl;}};int main(){base *pb = new derive(1, 2);pb -> display();derive *pd1 = static_cast<derive*>(pb);//子类指针->父类指针,静态类型转换,正确但不推荐derive *pd2 = dynamic_cast<derive*>(pb);//子类指针->父类指针,动态类型转换,正确pd1 -> display();pd2 -> display();base *pb1 = new base(3);pb1 -> display();derive *pd3 = static_cast<derive*>(pb1);//父类指针->子类指针,静态类型转换,危险!访问子类成员可能越界derive *pd4 = dynamic_cast<derive*>(pb1);//父类指针->子类指针,动态类型转换,安全,结果是NULLpd3 -> display();if(pd4 != NULL)pd4 -> display();return 0;}


4)reinterpret_cast

仅仅重新解释类型,但没有进行二进制的转换:
1. 转换的类型必须是一个指针、引用、算术类型、函数指针或者成员指针。
2. 它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。但不能将非32bit的实例转成指针。
3. 最普通的用途就是在函数指针类型之间进行转换。
4. 很难保证移植性。


总结:
去const属性用const_cast。
基本类型转换用static_cast。
多态类之间的类型转换用dynamic_cast。
不同类型的指针类型转换用reinterpret_cast。


4、typedef和define的详细区别(参考自 http://developer.51cto.com/art/201104/256060.htm (typedef和define的详细区别))


我们可以使用 typedef 自定义数据类型。常用于:

1)为已有数据类型取“别名”,如我们比较熟悉的 size_t:

typedef size_t unsigned int;

2)用typedef定义数组、指针、结构等类型将带来很大的方便,不仅使程序书写简单而且使意义更为明确,因而增强了可读性,如:

typedef int array[10];       //array 等价于 int[10]typedef void (*Fun)(void);   //Fun 等价于 指向形参为 void,返回值为 void 的函数指针


#define是预编译指令,而 typedef 被当成语句处理

二者区别:

_1、首先,二者执行时间不同

关键字 typedef 在编译阶段有效,由于是在编译阶段,因此 typedef 有类型检查的功能。

#define是宏定义,发生在预处理阶段,也就是编译之前,它只进行简单而机械的字符串替换,而不进行任何检查。

_2、功能不同

typedef 用来定义类型的别名,这些类型不只包含内部类型(int,char等),还包括自定义类型(如struct),可以起到使类型易于记忆的功能。


#define不只是可以为类型取别名,还可以定义常量、函数、编译开关等。


_3、作用域不同

#define具有文件作用域,作用于该文件里的每个地方;

typedef有自己的作用域,和变量有点类似,有全局和局部之分,但和 #define 一样其作用域不会超出该文件。


_4、对指针的操作

二者修饰指针类型时,作用不同。

Typedef int * pint;  #define PINT int *  Const pint p;//p不可更改,p指向的内容可以更改,相当于 int * const p;  Const PINT p;//p可以更改,p指向的内容不能更改,相当于 const int *p;或 int const *p;  pint s1, s2; //s1和s2都是int型指针  PINT s3, s4; //相当于int * s3,s4;只有一个是指针。 

其实,typedef和define末尾的标号也是不一样的,希望大家不要忽略这一点。
0 0
原创粉丝点击