C++虚函数 多态 纯虚函数 区别

函数调用的时候,编译器总是要为函数的每个参数制作临时复本.为参数分配
的空间来自程序的"栈",并用实际参数的值来初始化形式参数.形式参数的值是实
际参数值的独立副本.这些分配的空间在函数终止时被撤销(即在执行return语句
或已经到达函数体结尾时);
   
1.在按照指针传递参数时,应该指明:
  a. 在调用中对实际参数使用求地址运算符&;
  b. 函数头(和函数原型)中参数为指针类型(即要使用指针符号*);
  c. 在函数体内对该参数使用间接引用运算符(即改变指针指向的内容);

2.在按照引用传递参数时,应该指明:
  a. 在调用时不用地址运算符,直接传递实际参数名;
  b. 函数头(和函数原型)中参数为引用类型(即要使用引用符号&);
  c. 在函数体内无需间接引用操作的参数名(即直接改变引用的内容);

软件工程提示:
    在需要改变参数值的时候,尽量按引用传递参数,尽量避免通过指针传递参数.


虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一
个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。
     ——摘自MSDN
多态:对象能够对同一个函数调用做出不同响应。多态是通过纯虚函数实现的。
通过基类指针(或引用)来请求使用虚拟函数时,C++会在与对象关联的派生类
中选择正确的改写过的函数。

有纯虚函数的类是抽象类,不能生成对象,只能派生。他派生的类的纯虚函数没有被改写,那么,它的派生类还是个抽象类。

纯虚函数的意义,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的缺省实现。所以类纯虚函数的声明就是在告诉子类的设计者,“你必须提供一个纯虚函数的实现,但我不知道你会怎样实现它”。

虚函数的情况和纯虚函数有点不一样。照例,派生类继承了函数的接口,但简单虚函数一般还提供了实现,派生类可以选择改写(override)它们或不改写它们。

虚函数的意义,每个类必须提供一个可以被调用的虚函数,但每个类可以按它们认为合适的任何方式处理。如果某个类不想做什么特别的事,可以借助于基类中提供的缺省处理函数。也就是说,虚函数的声明是在告诉子类的设计者,"你必须支持虚函数,但如果你不想写自己的版本,可以借助基类中的缺省版本。"

纯虚函数:
纯虚函数在基类中定义时没有定义属性,也就是说,虚函数在基类中就只有一个函数名,这是与虚函数的一个区别.
纯虚函数最显著的特征是:它们必须在继承类中重新声明函数(不要后面的=0,否则该派生类也不能实例化),而且它们在抽象类中往往没有定义。

纯虚函数的意义,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的缺省实现。所以类纯虚函数的声明就是在告诉子类的设计者,“你必须提供一个纯虚函数的实现,但我不知道你会怎样实现它”。 (这里也是与虚函数的根本区别之处)

抽象类:

声明了纯虚函数的类是一个抽象类,该类必须被用来继承,他的子类默认的函数中也至少有一个父亲带来的纯虚函数,这些纯虚函数如果继续没有提供实际代码,那么这个子类仍然还是抽象类,直到所有的函数里(继承的或者另外提供的)不存在纯虚函数为止才脱离抽象类的范围,成为可以实例化的类。所以,用户不能创建抽象类的实例,只能创建它的派生类的实例。含有纯虚函数的子类也是抽象类(abstract)
它不声明对象,只能声明该类的指针.


基类声明的虚函数,在派生类中也是虚函数,即使不再使用virtual关键字。

一个函数声明为纯虚后,纯虚函数的意思是:我是一个抽象类!不要把我实例化!纯虚函数用来规范派生类的行为,实际上就是所谓的“接口”。它告诉使用者,我的派生类都会有这个函数。

在你设计一个基类的时候,如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的。从设计的角度讲,出现在基类中的虚函数是接口,出现在派生类中的虚函数是接口的具体实现。通过这样的方法,就可以将对象的行为抽象化。

 

----------------------------------------------------------------------------------------------------------------

假如我们之前有了这几个类,好比我们以后程序中需要的工作类:

class Base
{
public:
 Base(){cout << "Base" << endl;}
 virtual ~Base(){cout << "~Base" << endl;}
 void show()
 {
  cout << "this is Base" << endl;
 }
};

class ChildA : public Base
{
public:
 ChildA(){cout << "ChildA" << endl;}
 virtual ~ChildA(){cout << "~ChildA" << endl;}
 void show()
 {
  cout << "this is ChildA" << endl;
 }
};

class ChildB : public Base
{
public:
 ChildB(){cout << "ChildB" << endl;}
 virtual ~ChildB(){cout << "~ChildB" << endl;}
 void show()
 {
  cout << "this is ChildB" << endl;
 }
};

一、例子1:

                                   先从简单的来,探索下构造和析构:

(1)。Base bb;          

这样做,实例了一个对象,调用构造,最后程序结束时走析构。

(2)。Base* bb;

这样做,申请了类指针,它不会被分配内存,它不会构造,也不会析构,但是可以使用它的成员函数或方法。但方法实现采用什么结构和模式,就要值得考虑下。

(3)。Base* bb = new Base();

这样做,申请了类指针,并且为他分配内存地址,调用构造,和(1)相比,可以随时delete掉这个对象来释放内存,对中大型工程来说很受欢迎。

                                  上面已经介绍有关类构造和析构的处理机制,下边就有关继承了,需要注意的就是顺序和内存占有度:

(4)。ChildA ca;

这样做,由于继承了一个类,所以要先调用父类的构造,再调用自己的构造,退出的时候先自己的析构,然后是父亲的析构。

(5)。ChildA *ca;

这样做,指针没有指任何内存单元,所以它其实什么也没有。

(6)。ChildA *ca = new ChildA();

这样做,其实和(1)+(3)一样,不过对于释放内存要补充的是,如果没有手动的释放,既delete掉,程序退出之前不会释放,它所占有的内存会随着程序的终止而终止,这点很重要。

                                 再来,就是对多态了,这里是OOP的重点,也是难点:

Base *bb=new ChildA();

bb->work();

这样做,由于work是虚,所以去掉了静态链接,应上边的话,就是基类指针指向派生类,则会使用派生出来的方法。

/*

略,其实想写的很多。。。

*/

 

 

 

 

先到这里吧,还有其他的要忙。现在满大街的书上都有这方面的教程,看归看,但还是要多练,我也欠练,记了好多几遍,到最后还是忘得差不多了,以后有机会再完善吧。

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小雄哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值