提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
    • 继承性反映的是类与类之间的层次关系,多态性则是考虑这种层次关系以及类自身特定成员函数之间的关系来解决行为的再抽象问题。多态性有两种表现形式一种是不同的对象在收到相同的消息时,产生不同的动作,主要通过虚函数来实现;另一种是同一对象收到相同的消息却产生不同的函数调用,主要通过函数重载来实现。本章将讨论多态性的主要内容:虚函数和动态联编;
  • 一、静态联编与动态联编
    • 静态联编
    • 动态联编
    • 虚函数
        • 虚函数的作用
      • 虚函数与一般重载函数的区别
      • 继承虚属性
        • 示例虚函数对他的基类的函数没有影响
      • 成员函数中调用虚函数
      • 构造函数和析构函数中调用虚函数
      • 纯虚函数和抽象类
        • 纯虚函数
        • 抽象类
      • 虚析构函数
        • 虚析构函数的定义和使用
        • 虚析构函数的必要性
  • 应用实例

前言

继承性反映的是类与类之间的层次关系,多态性则是考虑这种层次关系以及类自身特定成员函数之间的关系来解决行为的再抽象问题。多态性有两种表现形式一种是不同的对象在收到相同的消息时,产生不同的动作,主要通过虚函数来实现;另一种是同一对象收到相同的消息却产生不同的函数调用,主要通过函数重载来实现。本章将讨论多态性的主要内容:虚函数和动态联编;

一、静态联编与动态联编

多态性就是同一符号或名字在不同情况下具有不同解释的现象,既是指同一个函数的多种形态。c++可以支持两种多态性,编译时的多态性和运行时的多态性。
对一个函数的调用,要在编译时或在运行时确定将其
连接上相应的函数体的代码,这一过程称为函数联编(简称联编)。c++中两种联编形式:静态联编和动态联编。

静态联编

  静态联编是指在程序编译连接阶段进行的联编。编译器根据源代码调用固定的函数标识符,然后由连接器接管这些标识符,并用物理地址代替他们。这种联编又被称为早期联编,应为这种联编工作是在程序运行之前完成的。静态联编所支持的多态性称为编译时的多态性,当调用重载函数时,编译器可以根据调用时所使用的实参在编译时就确定下来应调用哪个函数。下面来看在层次关系中一个静态联编的例子。
#include<iostream>
const double PI = 3.14;
using namespace std;
class Figure         //定义基类;
{public:Figure() {};double area() const { return 0.0; }
};
class Circle :public Figure           //定义派生类,公有继承;
{public:Circle(double myr) { R = myr; }double area() const { return PI * R * R; }
protected:double R;
};
class Rectangle :public Figure   //定义派生类,公有继承;
{public:Rectangle(double myl, double myw) { L = myl; W = myw; }double area() const { return L * W; }
private:double L, W;
};
int main()
{Figure fig;                //基类Figure对象;double area;area = fig.area();cout << "Area of figure is" <<area<< endl;Circle c(3.0);            //派生类Circle对象;area = c.area();cout << "Area of Circle is" << area << endl;Rectangle rec(4.0, 5.0);area = rec.area();cout << "Area of Rectangle is" << area << endl;return 0 ;
}

上面的程序我们可以看出,在静态联编中,其实就是对重载函数的使用。定义不同的对象,通过对象来引用不同的函数从而实现我们要实现的功能。
静态联编的最大优点就是速度快,运行时的开销仅仅是传递参数,执行函数调用,清除等。不过,程序员必预测在每一种情况下所有的函数调用时,将要使用那些对象。这样,不仅有局限性,有时也是不可能实现的。
下面的程序就会说明这种情况:

#include<iostream>
using namespace std;
const double PI = 3.14;
class Figure
{public:Figure() {};double area() const { return 0.0; }
};
class Circle :public Figure             //定义派生类,公有继承;
{public:Circle(double myr) { R = myr; }double area()const { return PI*R*R; }
protected:double R;
};
class Rectangle :public Figure               //定义派生类,公有继承;
{                               public:Rectangle(double myl, double myw) { L = myl, W = myw; }double area()const { return L * W; }
private:double L, W;
};
void func(Figure &p)         //形参为基类的引用;
{cout << p.area() << endl;
}
int main()
{Figure fig;cout << "Area of figure is";func(fig);Circle c(3.0);   //Circle派生类对象;cout << "Area of Circle is";func(c);Rectangle rec(4.0, 5.0);cout << "Area of Rectangle is";func(rec);return 0;
}


上面的程序没有报错,但是结果正确,那是为什么?
在编译时,编译器将函数中的形参p所执行的area()操作联编到Figure类的area()上,这样访问的知识从基类继承来的成员。
那有没有什么方法来改变这种局限性喃?

动态联编

动态联编是指在程序运行时进行的联编。只有向具有多态性的函数传递一个实际对象的时候,该函数才能与多种可能的函数中的一种来联系起来。这种联编又被称为晚期联编。
动态联编所支持的多态性被称为运行时多态性。在程序代码中要知名某个成员函数具有多态性需要进行动态联编,且需要关键字virtual来标记。这种用virtual关键字标记的函数称为虚函数.

动态联编的优点 动态联编的缺点
增强了编程灵活性,问题抽象性和程序易维护性 函数调用顺序慢

虚函数

虚函数的作用

虚函数是一个成员函数,该成员函在基类内部声明并且被派生类重新定义。为了创建虚函数,应在基类中该函数生命的前面加上关键字virtual。
virtual<返回值类型><函数名>(<形式参数>)
{
《函数体》
}
如果某类中一个成员函数被说明为虚函数,这便意味着该成员函数在派生类中可能存在不同的实现方式。当继承包含虚函数的类时,派生类将重新定义该虚函数衣服和自身的需要。从本质上讲,虚函数实现了“相同界面,多种实现”的理念。而这种理念时运行时的多态性的基础,既是动态联编的基础。
动态联编需要满足的条件:
(1).类之间满足类型兼容规则;
(2).要声明虚函数;
(3).要由成员函数来调用或者是通过指针,引用来访问虚函数。

#include<iostream>
using namespace std;
const double PI = 3.14;
class Figure
{public:Figure() {};virtual double area()const { return 0.0; }        //定义为虚函数;
};
class Circle :public Figure                          //定义派生类,公有继承;
{
public:Circle(double myr) { R = myr; }virtual double area()const { return PI * R * R; }
protected:double R;
};
class Rectangle :public Figure           //定义派生类,公有继承方式;
{public:Rectangle(double myl, double myw) { L = myl, W = myw; }virtual double area()const { return L * W; }
private:double L , W;
};
void fun(Figure& p)                 //形参为积基类的引用;
{cout << p.area() << endl;
}
int main()
{Figure fig;cout << "Figure of area is";fun(fig);Circle c(3.0);cout << "Area of Circle is";fun(c);Rectangle rec(4.0, 5.0);cout << "Area of Retangle is";fun(rec);return 0;
}


这个时候我们发现,答案正确了。看到这里,有没有一种熟悉的感觉,和我们前面学的虚基是不是相似。所以这里我们以可以理解为动态联编之所以能够不断地联编,就是因为成员函数产生了副本。

虚函数与一般重载函数的区别

乍一看,上面的程序,虚函数类似于重载函数。但它不属于重载函数,虚函数与一般的重载函数的区别:

虚函数 重载函数
虚函数不仅要求函数名相同,而且要求函数的签名,返回类型也相同。也就是说函数原型必须完全相同,而且虚函数的特性必须是体现在基类和派生类的类层次结构中 重载函数只要求函数有相同的函数名,并且重载函数 是在相同作用域定义的名字相同的不同函数
虚函数只能是非静态成员函数 重载函数可以是成员函数或友元函数
构造函数不能定义为虚函数,析构函数能定义为虚函数 构造函数可以重载,析构函数不可以
虚函数是根据对象来调用的 重载函数调用是以传递参数 序列的差别 来调用
虚函数是在运行时联编 重载函数是在编译时联编

继承虚属性

基类中说明的虚函数具有自动向下传给他的派生类的性质。不管经历多少派生类层,所有界面相同的函数都熬吃虚特性。因为派生类也是基类。
在派生类中重新定义虚函数时,要求与基类中说明的虚函数原型完全相同,这时对派生类的虚函数中virtual说明可以省略,但是为了提高程序的可读性。为了区分重载函数,而把一个派生类中重定义基类的虚函数称为覆盖。
示例:

#include<iostream>
using namespace std;
class Base
{public:virtual int func(int x)          //虚函数;{cout << "This is Base class";return x;}
};
class Subclass :public Base          //派生类,公有继承;
{public:int func(int x)                     //没有使用virtual关键字,但是依旧为虚函数;{cout << "This is Subclass class";return x;}
};
void fun(Base& x)
{cout << "x=" << x.func(5) << endl;
}
int main()
{Base bc;fun(bc);Subclass be;fun(be);return 0;
}


下面我们来分析一下虚函数的错误使用

#include<iostream>
using namespace std;
class Base
{public:virtual int func(int x)          //虚函数;{cout << "This is Base class";return x;}
};
class Subclass :public Base          //派生类,公有继承;
{public:virtual float func(int x)                     //函数返回类型不同{cout << "This is Subclass class";return x;}
};
void fun(Base& x)
{cout << "x=" << x.func(5) << endl;
}
int main()
{Base bc;fun(bc);Subclass be;fun(be);return 0;
}


这里派生类中的虚函数,仅仅只是使用了不同的返回类型,但是就报错了,这是用为在c++中,只靠返回类型不同的信息,进行函数匹配是模糊的。

#include<iostream>
using namespace std;
class Base
{public:virtual int func(int x)          //虚函数;{cout << "This is Base class";return x;}
};
class Subclass :public Base          //派生类,公有继承;
{public:virtual int func(float x)                     //函数形参不同{cout << "This is Subclass class";return x;}
};
void fun(Base& x)
{cout << "x=" << x.func(5) << endl;
}
int main()
{Base bc;fun(bc);Subclass be;fun(be);return 0;
}

如果派生类与基类的虚函数仅函数名相同,其他不同,则c++认为是重定义函数,是隐藏的,丢失了虚特性。
一个类中的虚函数说明只对派生类中重定义的函数有影响,对他的基类并没有影响。

示例虚函数对他的基类的函数没有影响
#include<iostream>
using namespace std;
class Base
{public:int func(int x)             //不是虚函数;{cout << "This is Base class";return x;}
};
class Subclass :public Base
{public:virtual int func(int x)              //虚函数;{cout << "This is Subclass class";return x;}
};
class Subclass2 :public Subclass
{public:int func(int x)                //自动成为虚函数;{cout << "This is Baseclass2 class";return x;}
};
int main()
{Subclass2 sc2;Base& bc = sc2;cout << "x=" << bc.func(5) << endl;Subclass& sc = sc2;cout << "x=" << sc.func(5) << endl;return 0;
}


从结果看出,func()的操作贝莱你白难道Subclasses类中,显然进行的是动态联编。

成员函数中调用虚函数

一个基类或派生类的成员函数中可以直接调用该类等级中的虚函数;

#include<iostream>
using namespace std;
class Base
{public:virtual void func1()         //虚函数{cout << "Tis is Base class";}void func2() { func1(); }     //调用了虚函数;
};
//一般类中的成员都是通过对象来调用,但是这里用虚函数就可以,除了虚函数,还有就是使用静态static关键字,表明他已经被被分配内存;
class Subclass :public Base
{public:virtual void func1(){cout << "This is Subclass func1()";}
};
int main()
{Subclass sc;sc.func2();return 0;
}

上面的例子中,我们可以得出,在满足公有继承的情况下,成员函数中调用虚函数使用的是动态联编;

构造函数和析构函数中调用虚函数

在构造函数和析构函数中调用虚函数时,采用静态联编,即它们所调用的虚函数是自己的类定义的函数。如果在自己的类中没有实现这个函数,则调用的时基类中虚函数,但绝不是任何在派生类中重新定义的虚函数。这是因为在建立派生类的对象时,它所包含的基类成员在派生类中定义的成员建立之前被建立。在对象撤销时,该对象所包含的在派生类中定义的成员要先与基类成员的撤销。
示例代码:

#include<iostream>
using namespace std;
class Base
{public:Base() { func1(); }       //构造函数调用虚函数,静态联编;virtual void func1()         //虚函数;{cout << "This is Base func1()" << endl;}virtual void func2(){cout << "This is Base func2()" << endl;}~Base() { func2(); }     //析构函数调用虚函数,静态联编;
};
class Subclass :public Base
{public:virtual void fnc1(){cout << "This is Subclass func1()" << endl;}virtual void func2(){cout << "This is Subclass func2()" << endl;}
};
int main()
{Subclass sc;cout << "Exit main" << endl;return 0;
}


上面的程序中,我们定义了一个派生类对象,派生类中没有构造函数,所以调用了基类中的构造函数,基类中的构造函数调用了它自身定义的虚函数,这里是静态联编。

纯虚函数和抽象类

当虚函数没有被派生类重新定义时,将使用基类中的虚函数。然而基类常用来表示一些抽象的概念,基类中没有有意义的虚函数定义。另外,在某些情况下,需要一个虚函数被所有派生类覆盖。为了处理上述两种情况,可以将虚函数定义为纯虚函数,相应的类就变成了抽象类。

纯虚函数

如果不能在基类中给出有意义的虚函数的实现,但又必须让基类为派生类提供一个公共界面函数。这时可以将它说明为纯虚函数,留给派生类去实现,说明纯虚函数的格式:
virtual 返回类型 函数名(参数)=0;
纯虚函数的定义是在虚函数定义的基础上再让函数等于0,这只是表示纯虚函数的形式,并不是说他的返回值为0;

#include<iostream>
const double PI = 3.14;
using namespace std;
class Figure
{public:Figure() {};virtual double area()const = 0;    //定义为纯虚函数;如果不是纯虚函数,就要返回一个double值;
};
class Circle :public Figure
{public:Circle(double myr) { R = myr; }virtual double area()const { return PI * R * R; }         //定义为虚函数;
protected:double R;
};
class Rectangle :public Figure
{public:Rectangle(double myl, double myw) { L = myl; W = myw; }virtual double area()const { return L * W; }   //定义为虚函数;
private:double L ,W;};
void fun(Figure& p)
{cout << p.area() << endl;
}
int main()
{Circle c(3.0);cout << "Area of cicle is";fun(c);Rectangle rec(4.0, 5.0);cout << "Area of rectangle is";fun(rec);return 0;
}

上面的代码中,Figure中的虚函数仅仅起到为派生类提供接口的作用。

抽象类

一个类可以说明多个纯虚函数,对于包含有纯虚函数的类被成为抽象类。一个抽象类只能作为基类来派生新类,不能说明抽象类的对象。应为抽象类中有一个或者多个函数没有被定义,也不能用作参数类型,函数返回类型或显示类型类型。但剋以说明指向抽象类对象的指针(和引用),以支持运行时的多态性;

例如:
Figure fig; //错误;
Figure func1();//错误;
int func2(Figure);  //错误;
void func&(Figure&p);//正确;

虚析构函数

一个类中经所有的成员函数总是有益的,它除了会增加一些资源开销,没有其他坏处。但设置虚函数有许多限制,一般只有非静态的成员函数才可以说明为虚函数。除此之外,析构函数也可以是虚函数,并且最好在动态联编的时候说明为虚函数;

虚析构函数的定义和使用

c++目前不支持构造虚构造函数。由于析构函数不能有参数,所以一个类只能有一个虚析构函数;

格式:
virtual ~<类名>()
入宫一个类的析构函数是虚函数,那么,由它派生而来的所有子类的析构函数也是虚函数。如果析构函数是虚函数,则这个调用是动态联编;

#include<iostream>
using namespace std;
class Base
{public:Base(){}virtual ~Base() { cout << "Base destructor is called" << endl; }
};
class Subclass :public Base
{public:Subclass(){}~Subclass();
};
Subclass::~Subclass()
{cout << "Subclass destructor is called" << endl;
}
int main()
{Base* b = new Subclass;delete b;return 0;
}

上面的程序中,我们构造了虚析构函数,那么我们构造虚析构函数的意义又在哪里?

虚析构函数的必要性

如果我们在类中定义了一个虚函数,我们在定义了一个对象后,如果要删除这个对象(delete),那么就会自动调用析构函数,也就是说,为了满足能够删除所有的成员函数,也就是构造虚析构函数;

#include<iostream>
using namespace std;
class Base
{public:Base(){}~Base() { cout << "Base destructor is called" << endl; }
};
class Subclass :public Base
{public:Subclass();~Subclass();
private:int* ptr;
};
Subclass::Subclass()
{ptr = new int;
}
Subclass::~Subclass()
{cout << "Subclass destructor is called" << endl;delete ptr;
}
int main()
{Base* b = new Subclass;delete b;return 0;
}


上面的程序我们会发现,用delete删除对象的时候,只调用了基类的析构函数,尾调用派生类的析构函数,,这造成ptr的内存未得到释放,现在我们将~Base()改为虚析构函数;看它的运行:

为什么把析构函数改为虚函数就会成功?这是因为将析构函数改为虚函数后,在撤销对象的时候,会先执行派生类的析构函数,由于这里采用了动态联编,所以才会得到这个结果;

应用实例

编写一个小型公司的工资管理程序,该公司主要有4类人员:经理。兼职技术人员,销售人员,销售经理。经理固定月薪8000,尖子技术人员100元每小时,销售员为当月销售额的4%,销售经理保底工资为5000,加上部门销售额的5%;
代码实现:

#include<iostream>
#include<string>
using namespace std;
//基类Employee的定义
class Employee
{protected:int no;  //编号;     string name; //姓名float salary;  //月星总额static int totalno;  //编号最大值
public:Employee(){no = totalno++;        //输入员工号加1;cout << "职工姓名:";cin >> name;salary = 0.0;   //总额赋值为0;}virtual void pay() = 0;   //计算月薪函数;virtual void display() = 0;  //输出员工信息函数;
};
//兼职技术人员类Technician的定义
class Technician :public Employee
{private:float hourlyrate;    //每小时酬金;int workhours;   //当月工作时数;
public:Technician()   //构造函数;{hourlyrate = 100;         //每小时酬薪100;cout << name << "本月工作时间:";cin >> workhours;}void pay(){salary = hourlyrate * workhours;    //计算月薪,按小时计算;}void display(){cout << "兼职技术员:" << name << ",编号:";cout << no << ",本月工资:" << salary << endl;}
};
//销售人员类Salesman的定义
class Salesman :virtual public Employee         //派生类,销售员;
{protected:float commrate;     //按销售额提取酬金的百分比;float sales;         //当月销售额;
public:Salesman()        //构造函数;{commrate = 0.04f;     //销售提成比例为4%;cout << name << "本月销售额:";cin >> sales;}void pay()           //计算销售员月薪函数;{salary = sales * commrate;     //月薪=本月销售额*销售提成比例;}void display()  //显示销售员信息函数;{cout << "销售员:" << name << ",编号:";cout << no << ",本月工资:" << salary << endl;}
};
class Manager :virtual public Employee
{protected:float monthlypay;      //固定月薪;
public:Manager(){monthlypay = 8000;}void pay()           //计算经理月薪总数;{salary = monthlypay;          //月薪总额;}void display(){cout << "经理:" << name << "编号";cout << no << ",本月工资:" << salary << endl;}
};
//销售经理类Salesmanager的定义:
class Salesmanager :public Manager, public Salesman
{public:Salesmanager(){monthlypay = 5000;commrate = 0.005f;cout << name << "所有部门月销售量:";cin >> sales;}void pay(){salary = monthlypay + commrate * sales;}void display(){cout << "销售经理:" << name << ",编号:" << no << ",本月工资:" << salary << endl;}
};
int Employee::totalno = 10000;
//主函数;
int main()
{Manager m1;Technician t1;Salesman s1;Salesmanager sm1;Employee* em[4] = { &m1,&t1,&s1,&sm1 };cout << "上述人员的基本信息:" << endl;for (int i = 0; i < 4; i++){em[i]->pay();em[i]->display();}return 0;
}


上面的程序就是一个简单的应用,公司的工资管理系统。c++的灵魂就是动态联编,所以掌握好动态联编是学好c++的基础。

c++面向对象程序设计基础教程————多态性和虚函数相关推荐

  1. PHP-----PHP程序设计基础教程----第三章函数

    3.1 初识函数 3.1.1 函数的定义 语法: function 函数名([参数1,参数2,......]) {函数体 } (1)function:在声明函数时必须使用的关键字 (2)函数名:创建函 ...

  2. C++面试题-面向对象-多态性与虚函数

    C++面试题-面向对象-多态性与虚函数 问:在C++程序中调用被C编译器编译后的函数,为什么要加extern "C"? 答:C++语言支持函数重载,C语言不支持函数重载.函数被C+ ...

  3. 只能输入字母的c语言程序设计教程课后答案,c语言程序设计基础教程_习题答案20120319...

    <c语言程序设计基础教程_习题答案20120319>由会员分享,可在线阅读,更多相关<c语言程序设计基础教程_习题答案20120319(54页珍藏版)>请在技术文库上搜索. 1 ...

  4. java界面编辑教程_java程序设计基础教程第六章图形用户界面编辑.docx

    java程序设计基础教程第六章图形用户界面编辑.docx 还剩 27页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,很抱歉,此页已超出免费预览范围啦! 如果喜欢就下载吧,价低环保! 内容要 ...

  5. 电大java语言与面向对象程序设计基础_6406Java语言与面向对象程序设计基础A卷...

    6406Java语言与面向对象程序设计基础A卷 试卷编号:6406 座位号 浙江广播电视大学2006年春季学期开放教育专科期末考试 <Java语言与面向对象程序设计基础>试题 2006年7 ...

  6. 创意c语言程序设计,重庆理工大学-c语言程序设计基础教程_习题答案(纪纲_金艳).doc...

    重庆理工大学-c语言程序设计基础教程_习题答案(纪纲_金艳).doc 还剩 56页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,很抱歉,此页已超出免费预览范围啦! 如果喜欢就下载吧,价低环 ...

  7. 《Java Web程序设计基础教程》简介

    本书是关于Java Web开发的基础教程,共分15章.第1章介绍如何构建Java Web应用:第2.3章介绍如何解决Java Web应用的输入和输出问题:第4-6章对登录功能进行了3次重构,这也是Ja ...

  8. c语言程序设计勘误,《程序设计基础教程(C语言)》勘误表

    <<程序设计基础教程(C语言)>勘误表>由会员分享,可在线阅读,更多相关<<程序设计基础教程(C语言)>勘误表(2页珍藏版)>请在人人文库网上搜索. 1 ...

  9. 头歌C++面向对象 - 类的多态性与虚函数

    C++ 面向对象 - 类的多态性与虚函数 一.实训目的 1.掌握虚函数的基本使用. 2.掌握虚析构函数的使用. 3.掌握纯虚函数和抽象类的使用. 二.实训内容 1.人与复读机 设计人类.英语学生类和复 ...

最新文章

  1. 七年级上册数学计算机教学视频,七年级上册数学教学视频免费
  2. [转载]大型网站架构演变和知识体系
  3. linux rpm安装软件笔记
  4. Bootstrap 折叠插件Collapse 选项
  5. SpringBoot (15)---事务处理
  6. 能给别人吗_《奇葩说》观点引热议:一种能看透别人心思的能力,你敢要吗?...
  7. python画散点图
  8. 闲谈安全测试之IAST
  9. 腾讯云服务器SSH密匙登录教程
  10. c语言计算个人所得税作业,C语言用文件计算个人所得税
  11. average函数python_在Python3 numpy中mean和average的区别详解
  12. Matlab 7 win7安装步骤
  13. 关于中部城市人才外流问题的思考
  14. 希迪智驾发布“V2X+智慧高速”方案,打造智慧高速
  15. sql操作access时出现 MSDTC错误,服务器 'SERVER' 上的 MSDTC 不可用。
  16. 玩转开发板--Linux系统移植至开发板fl2440实践过程
  17. 使用gn创建HelloWorld工程
  18. OPPO A7X 刷机小结
  19. sklearn专题四:降维算法
  20. 2020年软考初级网络管理员备考指南

热门文章

  1. 从《蜗居》的小贝学C++谈起:懒惰总会有理由
  2. 哈尔滨工程大学计算机调剂信息,2020年哈尔滨工程大学信息与通信工程学院考研调剂信息...
  3. Performance Metrics(性能指标2)
  4. C语言练习:多种方法实现分段函数求值
  5. 10秒钟 让XP/2003开机画面变脸
  6. mysql 服务器如何优化_mysql服务器优化
  7. keras train_on_batch详解(train_on_batch的输出输入详解,train_on_batch多GPU训练详解,自定义学习率调整策略)
  8. LOJ #10166. 数字游戏【数位DP】
  9. 图片处理+高速计算机配置,谁能给我推荐个专业处理图片的计算机配置么
  10. CUDA PYTHON 并行计算基础