我编写了一个程序,其中变量的类型在编译时未知。在运行时向用户询问。在第一个程序版本中,重复了部分代码。修改涉及所使用的变量类型。在第二和第三个程序版本中,使用了基于模板的方法和基于多态性的方法(具有抽象类和虚函数)。
所有程序都运行良好。但我想知道这两种方法的优缺点是什么,什么时候一种方法比另一种方法最合适,以及是否存在另一种方法?
为了澄清我的问题,下面是一个例子:
第一个版本(有代码重复)
#include <iostream>
using namespace std;
class Shape {
public : float lgth;
public : void getLgth() { cin >> lgth;}
};
class Square : public Shape {
public : float calcArea() { return lgth * lgth; }
};
class Circle : public Shape {
public : float calcArea() { return 3.14 * lgth * lgth; }
};
int main() {
char shape_type;
cout <<"Shape Type (c/s) ? ";
cin >> shape_type;
if (shape_type == 'c') {
Circle my_shape;
cout << "Shape Length ? ";
my_shape.getLgth();
cout << "Shape Area : " << my_shape.calcArea() << endl;
}
else if (shape_type == 's') {
Square my_shape;
// --- Code repetition ---
cout << "Shape Length ? ";
my_shape.getLgth();
cout << "Shape Area : " << my_shape.calcArea() << endl;
}
return 0;
}
基于模板的方法
添加模板
template <typename TShape>
// requires(std::derived_from<TShape, Shape>) // C++20 constraint
void do_job()
{
TShape my_shape;
std::cout << "Shape Length ?";
my_shape.getLgth();
std::cout << "Shape Area : " << my_shape.calcArea() << std::endl;
}
主要修改如下
if (shape_type == 'c') {
do_job<Circle>();
} else if (shape_type == 's') {
do_job<Square>();
}
基于多态性的方法
#include <iostream>
using namespace std;
// abstract class : Shape
// virtual function : calcArea
class Shape {
public :
float lgth;
virtual ~Shape() = default;
void getLgth() { cin >> lgth; }
virtual float calcArea() = 0;
};
class Square : public Shape {
public :
float calcArea() override { return lgth * lgth; }
};
class Circle : public Shape {
public :
float calcArea() override { return 3.14 * lgth * lgth; }
};
int main() {
char shape_type;
Shape* my_shape;
cout << "Shape Type (c/s) ? ";
cin >> shape_type;
if (shape_type == 'c') {
my_shape = new Circle;
}
else if (shape_type == 's') {
my_shape = new Square;
}
else {
cout << "Unknown type!" << endl;
return -1;
}
cout << "Shape Length ? ";
my_shape->getLgth();
cout << "Shape Area : " << my_shape->calcArea() << endl;
delete my_shape;
return 0;
}
没有任何优点和缺点。模板和继承是解决不同问题的不同机制。有时,您可以在其中一种更合适时强制使用另一种,特别是在非常简单的示例中,您最终只对对象做一件事,但一般来说,最好使用模板,其中模板是合适的解决方案,而继承是继承是合适的解决方案。
从或多或少的理论角度来看:模板为不同的接口提供了通用的实现,继承为通用的接口提供了不同的实现。例如,在标准中,
std::vector
是一个模板:例如,std::vector<double>
提供函数push_back( double )
,而std::vector<int>
提供push_back( int )
。但这两个函数具有相同的实现。另一方面,尽管 std::ostream
和 std::ofstream
的输出字符(实现)完全不同,但 std::ostringstream
的接口完全相同。
另一个重要的区别是模板在编译时解析,而虚函数在运行时解析。这意味着调用函数模板(或类模板的成员函数)通常会更快(但这可能会因需要额外的代码来显式选择要调用的函数而被抵消),并且可能有更多的可能性编译器来分析正在发生的事情,以便更好地进行错误检查或优化。另一方面,这意味着任何调用点只能调用该函数的一个特定实例;如果您有多个调用站点并希望在每个调用站点调用不同的函数,则模板要求您在每个调用站点显式确定类型,并为每个不同的函数提供单独的函数调用。