C++-使用模板解决问题

    xiaoxiao2021-03-25  116

    模板是非常灵活的事物。是C++众多复杂思想中较为容易的一个,但是也是非常有用的。

    注意:模板既可以在头文件中定义,也可以在你使用它们的C++源码文件中定义。定义它,又不能像正常函数和类那样定义。

    1 模板泛化

    假设你想在一个结构体包含一个对象数组:

    struct{ object a[500]; }

    但是现在你又想要一个double数组:

    struct{ double a[500]; }

    或者任何其它类型的数组。模板允许你使用通配符代替真实的类型。

    template <typename T> struct vector { T a[500]; }

    模板完全是在编译时有用。也就是说,模板意味着更长的编译时间,但是不会影响你的应用程序的执行时间。因为当程序被编译时,模板完成实例化。

    template<typename T> struct vector { T f(); }; struct Abstract_Base { virtual void func() = 0; }; struct Derived { virtual void func() {} }; int main() { vector<Derived> vector_of_deriveds; }

    经过编译器编译后的代码为:(注意:这不会真实发生,但是遵循通用思想)

    struct vector<Derived> { Derived f(); }; struct Abstract_base .... and on like normal.

    对于你在模板对象中你实例化的特定类型。一个特定的类将会被创建,适应它。如果你使用没有vector<>对象,也就没有no vector<>类被创建。

    int main() { vector<Abstract_Base> vector_of_abstract; }

    因为C++的规则,抽象基类(ABC)不能被创建。它有未定义的函数,且C++无法轻易地保证ABC不会调用它们。(一个指针能够隐藏ABC真正的身份)。 只要ABC遵循任何一个其它对象的规则,就可以作为一个类型名被使用。但是,这里却有一个问题。假设创建一个这样的类:

    struct vector<Abstract_Base> { Abstract_Base a[500]; };

    这将会违反C++的规则,且会在编译时引发一个错误。因为抽象基类是不能创建对象的。

    2 模板继承

    模板可以像正常类那样被派生:

    template <typename T> class Base { virtual T* clone() const; }; template<typename T> class Derived : public Base<T> // 注意语法 { virtual T* clone() const; };

    这是合法的代码。T* clone()会在派生类Derived中被重写。 注意:有一些编译器,像gcc的某些版本(当然了,并不是现在的,主流的版本),如果不使用参数调用父类的基类,将会给出错误。只需要在前面加上this->指针就好了。

    3 当特化时,模板也是很有用的

    template <typename T> class Object { protected: T x; public: T* other_func(); }; class Derived : public Object <int> { public: int some_func() { return x; // 合法的 } }; template<> class Object<bool> { public: void func() {} // 不必存在于任何实例化中 // 这会创建一个完全不同于Object<int>的类 // 一旦你特化后,你将不会借用到任何Object<>的东西 }; int main() { Derived o_i; Object<bool> o_b; o_b.func(); Object<Object<double> > o_o; return o_i.some_func(); }

    这个也是合法的代码。Object 不用和任何其它的Object<任何其它类型> 相同。

    4 模板也可以有多个参数

    template<typename F, typename L> struct cons { F first; L last; }; int main() { cons<int,double> zebra; return zebra.first; }

    5 函数也可以模板化

    // 注意: 在这里,你可以使用class代替typename template<typename T> T* clone(const T& t) { return new T(t); }

    编译器会通过函数的第一个参数侦测到T是什么类型。它并不会提升T类型,除非你自己手动转换:

    int main() { std::string* temp; temp = clone<std::string>("string-thang"); // this casts from a char* to a std::string return 0; }

    6 元编程与模板

    模板可以应用在元编程中。使用它,你能够不用遍历,创建大的,编译列表等等;所有的都不以牺牲运行效率为前提。(有时候,还会提升运行效率)。 这是一个编译时进行链接的链表例子:

    template<typename F, typename L> struct cons { F first; L last; }; struct nil {}; //empty class Console { private: typedef //here's the magic cons < int, cons < double, cons < vector < int >, nil > > > list_of_types; list_of_types l_o_s; public: Console(); };

    重要的是,你可以在程序编译的时候处理你的对象列表。也可以使复杂的东西简单化。 当你在类中处理大量的对象时,你需要为每一个编写一个独一无二的名字,像OBJ_1, OBJ_2, … OBJ_31。这样,对于编程者来说是有意义的,但是修改它们的时候却是非常麻烦。

    Console::Console() { parse(l_o_s); } typename <typename F, typename L> void parse(cons<F,L>& listing) { add_pointer(&listing.first); parse(listing.last); } void parse(nil&) {}

    模板也可以实现在编译时进行计算:

    template <unsigned int x> struct factorial { static const unsigned int result = factorial<x-1>::result * x; }; template<> struct factorial<0> { static const unsigned int result = 1; }; int main () { return factorial<3>::result; // exactly the same as return 6; }

    真诚希望你能明白模板能做什么。因为当正确使用它的时候,确实可以节省很多时间。

    转载请注明原文地址: https://ju.6miu.com/read-5242.html

    最新回复(0)