跳转至

08 类模板⚓︎

648 个字 71 行代码 预计阅读时间 4 分钟

类模板⚓︎

类模板(template class)是一种类,它将类型作为参数,实际运用时用这些参数来代替实际的类型。

举个🌰

Container.h
// template declaration
template <typename T>
class Container
{
    public:
        Container (T val);
        T getValue();

    private:
        T value;
};

template <typename T, typename U>
可以像这样在模板声明中列出多个模板参数,用来表示多个类型。

函数模板(template functions)的定义:

Container.cpp
#include "Container.h"

template <class T>
Container<T>::Container(T val)
{
    this->value = val;
}

template <typename T>
T Container<T>::getValue()
{
    return value;
}

在类模板中,如果类的成员函数返回的是类的某个成员变量,返回类型应设为typename ClassName<T1, T2, ...>::member_type,而不仅仅是 member_type

注意

由于在实例化之前,模板不会给出真正可运行的代码,所以还需要在 .h 文件里#<inlude> .cpp 文件。

const 的正确使用⚓︎

定义了 Student 类的接口,如下所示:

.h file
class Student
{
    private:
        using String = std::string;
        String name;
        String state;
        int age;
    public:
        Student(String name, String state, int age);
        void setName(String name);
        String getName();
        String getState();
        int getAge();
}

若我们定义了以下函数:

std::string stringify(const Student& s)
{
    return s.getName() + " is " + std::to_string(s.getAge()) + " years old.";
}

这会出现编译错误。虽然看起来我们只是用到了对象s里的一些成员,并没有对成员做什么修改,但是编译器就不知道s的成员函数(getName()getAge())是否会修改s的内容,所以编译器认为s有可能会被修改,因此不能用const修饰。

如果想要避免报错,就得按照高光部分修改(它们的函数实现也得加上const

.h file
class Student
{
    private:
        using String = std::string;
        String name;
        String state;
        int age;
    public:
        Student(String name, String state, int age);
        void setName(String name);
        String getName() const;
        String getState();
        int getAge() const;
}

上面的例子告诉我们:如果对象用const修饰,那么它只能和同样被const修饰的接口交互,这样编译器才敢肯定调用的接口不会修改对象(类)的内容。

事实上,类的成员函数的实现可以同时有常规版和 const 修饰版,两者只相差一个 const,下面以某个成员函数为例:

int& at(size_t index)
{
    return _array[index];
}

int& at(size_t index) const
{
    return _array[index];
}

看起来很简单,只要将常规版的内容复制粘贴一遍,之后补个 const 就行,可万一函数定义的代码很长很长,再复制粘贴的话可读性就很差了,因此这里有一个比较巧妙的方法来解决这一问题:使用 const_cast 运算符,格式为 const_cast<target_type> { expression },其中 target_type 应为类名。无论函数定义花括号内的主体部分有多少行代码,下面的 const 版本只要在花括号内写一句话就完工了:

int& at(size_t index) const
{
    // 先调用一遍 non-const 版,再将它“投射”到 const 版本
    return const_cast<IntArray&>(*this).at(value);
}

补充

  • auto 类型不会自动补上 const&,请注意!
  • 迭代器和 const 的关系:
    • const iterator:不能让迭代器递增,但是可以解引用或改变它底层的值
    • const_iteratoriterator 前用 const_ 修饰,中间没有空格:可以递增迭代器,但不能解引用或改变它底层的值
    • const const_iterator:什么都不可以改变

评论区

如果大家有什么问题或想法,欢迎在下方留言~