学习c++虚函数与纯虚函数

最近在学习c++ , 遇到虚函数和纯函数的一些问题,于是找了一些前辈们的成果分析 都是别人的研究成果。

C++ 虚函数与纯虚函数

纯虚函数定义如下: virtual functionname (parameter ) =0 ;
类的一个成员定位虚函数的实际意义在于让C++知道该函数并无意义,它的作用只是为了让派生类进行函数重载保留位置。纯虚好书的定义方法就是在类的虚函数后面加上 “ =0 ” 标记,类中一旦出现了纯虚函数的定义,那么此类为抽象类。


#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;

//抽象类定义
class abstractcls
{
   public:
     abstractcls (float speed, int total) //构造函数
     {
            abstractcls::speed=speed;
            abstractcls::total=total;
       }
     virtual void showmember()=0;    //纯虚函数的定义
   protected:
      float speed;
      int total;
};

class car public : abstractcls     //抽象类派生类
{
   public:
      //派生类的构造函数,实现基类的构造函数
      car(int aird, float speed, int total):abstractcls(speed,total)
      {
         car::aird=aird;
       }
   virtual void showmember() //派生类函数重载
   {
      cout<<speed<<"1"<<total<<"2"<<endl;
   }
protected:
   int aird;
};

调用如下:

int main(void)
{
car b(250,150,4);
     b.showmember();
return 0;
}

总结,什么情况下需要使用纯虚函数:
1,当想要在基类中抽象出一个方法,且该类被继承类而不能被实例化时。
2,基类的方法必须在派生类中被实现时。
3,多个对象具有公共的抽象属性,但却有不同的实现要求时。

实例2. 类A有两个纯虚成员函数 Lock(),unLock() 和一个虚析构函数


class A {
    public:
      virtual void Lock(void)=0;
      virtual void unLock(void)=0;
      virtual ~A(void);
};

类A实际上并没有实现这些函数,只是声明了他们。因为具有纯虚函数任何类不能用于创建运行时的对象,为了使用这个类,用户必须从这个抽象类派生出一个派生类来。而且为抽象类声明的每一个纯虚函数提供函数的定义与实现。
B类从A类派生,并提供了2个纯虚函数的定义。


class B :public A
{
public:
    B(void);
    ~B(void);
    virtual void Lock(void) {pthread_mutex_lock(x);}
    virtual void unLock(void) {pthread_mutext_unlock(x);}
protectd:
     pthread _mutex_t x;
};

B类通过POSIX 函数 pthread_mutext_lock() 和 pthread_mutext_unlock() 已经实现了两个纯虚函数。如果B只实现了其中的一个纯虚函数,那么B类仍然认为是一个纯抽象类,因为它仍然包含一个纯虚函数。

匿名委托的学习

什么是委托?MSDN上面是这样解释的

委托是一种定义方法签名的类型,可以与具有兼容签名的任何方法关联。您可以通过委托调用方法。委托用于将方法作为参数传递给其他方法。

那我们为什么要了解委托呢,不知道委托行不行吖?答案是不行的,委托是在.net中很基础的东西,包括事件是都是委托。

委托具有以下特点:

  • 委托类似于 C++ 函数指针,但它们是类型安全的。
  • 委托允许将方法作为参数进行传递。
  • 委托可用于定义回调方法。
  • 委托可以链接在一起;例如,可以对一个事件调用多个方法。
  • 方法不必与委托签名完全匹配。有关更多信息,请参见Covariance and Contravariance委托中的协变和逆变(C# 编程指南)
  • C# 2.0 版引入了匿名方法的概念,此类方法允许将代码块作为参数传递,以代替单独定义的方法。C# 3.0 引入了 Lambda 表达式,利用它们可以更简练地编写内联代码块。匿名方法和 Lambda 表达式(在某些上下文中)都可编译为委托类型。这些功能统称为匿名函数。

那我们学习委托能给我们带来什么好处捏?

我想好处大概有以下:

1.当然提高效率了,以前要写N个函数的地方,现在一个委托就搞定了。说白了,就是如何偷懒。

2.安全性得到了提高,都交给委托去办了,我们就不用担心了,当然这是在非常了解委托的前提下 。

在园子还有一篇关于委托的文章这里也有,文章讲了很多,,基本上从重构开始到模式,将委托和事件的思维过程完全重现,文中是一步步来思考,一步步解释委托,、由思考过程来导向,一番看下来,简直是享受啊,强烈建议新手看看,不要去MSDN上面死板的说明,强力推荐。

下面的代码来记录我学习委托的过程。

using System;

using System.Collections.Generic;

using System.Text;

namespace Artur.Study.MyDelegate
{
//好我们先定义一个委托,注意:
//该委托有一个string类型的形参跟方法SampleDelegateMethod签名一样
//何为方法的签名?
//方法的签名在声明该方法的类中必须唯一。

//方法的签名由方法的名称及其参数的数目、修饰符和类型组成。方法的签名不包含返回类型。
//见这里C# 语言规范 3.0 1.6.6节

//在方法重载的上下文中,方法的签名不包括返回值。但在委托的上下文中,签名的确包括返回值。换句话说,方法和委托必须具有相同的返回值。 见MSDN

delegate void SampleDelegate(string name);
class MyDelegateSample
{
staticvoid SampleDelegateMethod(string name)
{
Console.WriteLine(name);
}
staticvoid mysd(string mes)
{
Console.WriteLine(mes+”,the second time”);
}
staticvoid Main()
{
string myde=”Artur”;
//声明一个带参数的委托先

SampleDelegate sd1 = SampleDelegateMethod;
//注意等号的右边可以直接是函数名,这样最简便
//也可以是这样的,将函数名当做参数传递
//SampleDelegate s = new SampleDelegate(SampleDelegateMethod);
//再声明一个匿名委托,

SampleDelegate sd2 =delegate(string message)
{
Console.WriteLine(message);
Console.WriteLine(myde);
//匿名函数在这里访问了当前上下文的变量myde,该变量并不在函数方法内部。

};
//匿名委托不用以函数名为参数进行传递了,直接具体的实现函数

//可以将多个方法绑定到同一个委托变量,如下:

SampleDelegate mysd1;

mysd1 = SampleDelegateMethod;
mysd1 += mysd;

mysd1(“Hello World”);

//该委托会依次调用绑定的方法

Console.ReadLine();
}

}

}

hello World

this is a anonymous delegate

Artur
Hello World

Hello World,the second times

还有个例子在VS2008的QuickStart文件夹下面的。

using System;
using System.Collections.Generic;
using System.Text;

namespace AnonymousDelegate_Sample
{

//委托的定义必须与最终被调用的方法保持签名一致
//委托与最终调用的方法的签名必须一致

delegate decimal CalculateBonus(decimal sales);
//声明了一个委托的方法,返回类型为decimal,参数为decimal

//定义个一个类Enployee
class Employee
{
public string name;
public decimal sales;
public decimal bonus;
public CalculateBonus calculation_algorithm;
}

class Program
{

//定义了两种委托方法执行计算奖金
//一种是指定方法的委托,另一种是匿名委托

/*下面是第一个委托所指定的方法
* 声明了一个计算可执行的奖金算法
*/
static decimal CalculateStandardBonus(decimal sales) //静态的方法 签名跟委托一样的
{
return sales / 10;
}

static void Main(string[] args)
{

decimal multiplier = 2;
/*是声明一个在算法中用到的变量
* 该变量会被匿名委托访问
*/

CalculateBonus standard_bonus = new CalculateBonus(CalculateStandardBonus);
//定义个一个委托,将函数CalculateStandardBonus当做参数进行传递

CalculateBonus enhanced_bonus = delegate(decimal sales) { return multiplier * sales / 10; };
//再次定义一个委托,注意这里是匿名的委托,没有指定函数名,但是编译器怎么知道是函数CalculateStandardBonus呢
//答案是唯一的签名,返回类型和参数类型都为decimal只有函数CalculateStandardBonus()
// 实例化一些员工对象
Employee[] staff = new Employee[5];

for (int i = 0; i < 5; i++)
staff[i] = new Employee();

// 实例化赋值 Employees.
staff[0].name = “Mr Apple”;
staff[0].sales = 100;
staff[0].calculation_algorithm = standard_bonus;

staff[1].name = “Ms Banana”;
staff[1].sales = 200;
staff[1].calculation_algorithm = standard_bonus;

staff[2].name = “Mr Cherry”;
staff[2].sales = 300;
staff[2].calculation_algorithm = standard_bonus;
//上面的三个使用的是具体方法的委托
staff[3].name = “Mr Date”;
staff[3].sales = 100;
staff[3].calculation_algorithm = enhanced_bonus;

staff[4].name = “Ms Elderberry”;
staff[4].sales = 250;
staff[4].calculation_algorithm = enhanced_bonus;
//上面2个匿名委托

//为所有员工计算福利
foreach (Employee person in staff)
PerformBonusCalculation(person);

//显示全部员工的详细信息
foreach (Employee person in staff)
DisplayPersonDetails(person);

Console.Read();
}

public static void PerformBonusCalculation(Employee person)
{

//该方法利用委托存储person对象

//注意该方法可访问multiplier局部变量,尽管multiplier变量不在该方法内部
//即是具体的参数具体实现。
person.bonus = person.calculation_algorithm(person.sales);
}

public static void DisplayPersonDetails(Employee person)
{
Console.WriteLine(person.name);
Console.WriteLine(person.bonus);
Console.WriteLine(“—————“);
}

在这个QuickSatrt的例子,声明了两个委托来计算福利,一个是带具体方法名的委托,一个是匿名委托

总结:

1.委托实际上就是将函数当做传参数来传递

2.匿名委托与委托最大的区别就是能够访问当前上下文的变量,即使变量不在方法内部。

3.事件其实也是一种委托。

4.在委托中必须具有相同的方法签名,包括返回类型和参数。