C++知识点61——typename与class、模板编程与继承、模板类和友元、类模板与static成员

本文围绕C++模板编程展开,探讨了typename与class的异同,指出表示模板参数时二者无区别,仅class后表达式不正确时必须用typename,嵌套从属类在继承列表时不能用typename;还介绍了模板编程与继承、模板类和友元、类模板与static成员的相关规则及示例。

一、typename与class的异同

1、啥时候既可以使用typename,又可以使用class?

当表示模板参数的时候,二者没有区别

 

2、啥时候只能使用typename,不能使用class?

当模板内部的成员表示一个类型时(标识嵌套从属类型名称),必须使用typename,而不能使用class,如果不使用typename编译器会把从属嵌套的类型视为一个成员变量,在遇到*时,会把*解析成乘法,而不是解引用符号

虽然书上是这样写的,但是实际并不使这样,在g++ 7.5.0测试发现,即使表示一个模板参数的类型成员(标识嵌套从属类型名称),typename和class也没有明显差异

示例1

template <class T>
class mytest
{
	class T::val_type func(const T &c);
};

template <class T>
class T::val_type mytest<T>::func(const T &c)
{
	typename T::val_type *ptr;
    class T::val_type t;
	return typename T::val_type();
}

上述代码中的第10行必须加typename或者class关键字,否则,编译器会认为类型T中的static成员val_type与ptr相乘,而ptr没有定义,然而使用typename或者class都可以

但是,第11行只能用typename,不能用class,否则编译器报错

编译器提示,和class一起使用的表达式不正确,所以必须typename的原因是class后面的表达式不正确,而不是什么只能用typename不能用class

 

示例2

template <typename T>
void print(const T &con)
{
	class T::const_iterator pos;
	class T::const_iterator end(con.end());

	for (pos=con.begin();pos!=end;++pos)
	{
		cout<<*pos<<endl;
	}
}

上述代码负责打印容器中的元素,第4,5行代码虽然都是表示T中的迭代器类型,但是使用class和typename都可以

所以,使用模板参数中的类型成员时,用class还是typename没有区别,只有class后的表达式不正确时,才必须使用typename

所以,既然能使用class的地方都能使用typename,但是有的地方还不能使用class,所以,在模板编程中,只使用typename表示类型就对了

 

3、例外

当嵌套从属的类出现在继承列表中时,此时不能使用typename表示类型

 

二、模板编程与继承

如果一个类模板被当做基类,如果子类不是模板类,那么派生列表要指定基类的模板实参,如果子类也是个类模板,那么,子类的派生列表可以使用自身的模板参数来指定基类的模板实参

示例

template <typename T>
class base
{
};

template <typename T>
class derive:public base<T>
{
};

class derive2:public base<int>
{
};

如果子类是个类模板,并且派生列表中的基类使用了子类的模板参数,那么在子类成员函数中调用基类成员函数时,要指定作用域

template <typename T>
class base
{
public:
	void functest(){cout<<__func__<<"in base"<<endl;}
};

template <typename T>
class derive:public base<T>
{
public:
	void func(){
		functest();
		cout<<__func__<<"in derive"<<endl;
	}
};

上述代码无法编译通过

编译时,编译器提示functest是个依赖模板参数的函数,但是没有指明模板参数,所以编译器报错。所以解决办法有两个:

1、加上基类的作用域

emplate <typename T>
class derive:public base<T>
{
public:
	void func(){
		base<T>::functest();
		cout<<__func__<<"in derive"<<endl;
	}
};

2、在子类中重新定义一个functest

template <typename T>
class derive:public base<T>
{
public:
	void func(){
		functest();
		cout<<__func__<<"in derive"<<endl;
	}
	void functest(){cout<<__func__<<"in derive"<<endl;}
};

如果子类只是个普通类,但是要类模板做基类,那么必须将基类模板实例化

class derive2:public base<int>
{
public:
	void func(){
		functest();
		cout<<__func__<<"in derive"<<endl;
	}
};

此时编译器并不会报错,因为基类是个具体的类,创建该子类对象时,基类的this已经确定,可以找到基类的的functest

 

三、模板类和友元

如果一个类模板中包含一个友元,如果友元不是模板,那么,友元可以访问所有模板的实例,无须解释,和以前一样

如果友元是模板,那么友元模板的所有或者部分实例可以访问所有的类模板的实例

示例

template <typename T>
class friend1
{

};

template <typename T>
class friend2
{

};

template <typename T1>
class test2
{
	template <typename T2>
	friend class friend1;//类模板friend1的所有实例都是test2实例的友元

	friend class friend2<T1>;//只有用T1实例化的类才是test2的友元
};

 

四、类模板与static成员

类模板的每个实例类都各自拥有类模板中的static成员

示例

template <typename T>
class statictest
{
	static void func() {}
	static int si;
};

template <typename T>
int statictest<T>::si=0;

int main(int argc, char const *argv[])
{
	statictest<int> t;
	int i=t.si;
	//int i2=statictest::si;
}

因为static成员是各个实例化的模板类各自所有,而上述代码中的第15行没有指定模板参数,需要注释掉

static成员函数和其他类模板的成员函数一样,只有在被调用时才会被实例化产生代码

 

参考

《C++ Template》

《C++ Primer》

《Effective C++》

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

<think>好的,用户想让我介绍一下C++知识点,包括模板,还要举例。首先,我得确定用户的基础。可能用户已经有一定的C++基础,现在想深入学习模板相关的知识。或者用户可能刚开始学习,需要从基础讲起。需要涵盖哪些内容呢?的定义、成员函数、访问控制、构造函数析构函数、静态成员友元,然后模板部分包括函数模板类模板,可能还要讲模板特化可变参数模板。 用户提到要举例,所以每个知识点都需要配例子。比如的定义,可以举一个简单的学生,有私有成员变量公有方法。构造函数部分可以展示默认构造带参数的,以及初始化列表的使用。析构函数可能需要一个例子,比如释放资源的情况,虽然简单的例子可能用不上,但可以举一个输出信息的例子说明调用时机。 静态成员变量函数,可以举一个统计对象数量的例子。友元的话,可能需要一个函数访问私有成员的例子,或者友元的情况。 模板部分,函数模板可以举max函数的例子,类模板比如一个Box容器模板特化可以举针对特定型的优化实现,比如针对char*的特化。可变参数模板可能稍微复杂,可以举一个打印多个参数的例子,或者计算可变参数的。 需要注意的是,用户可能希望例子简洁明了,不要太复杂。要确保每个例子都能准确说明对应的知识点。比如在讲类模板时,实例化模板类的时候要展示如何指定型参数。还有模板的编译方式,头文件中包含定义,这点可能需要提到。 另外,用户可能对模板的适用场景不太清楚,可以简单说明模板在泛型编程中的作用,比如STL中的vector、list等都是用类模板实现的。这样用户能联系到实际应用。 可能用户在学习过程中遇到了一些问题,比如模板的编译错误,或者对友元的使用不太理解,所以需要解释清楚这些概念。同时,访问控制中的public、private、protected的区别也需要明确,尤其是在继承时的表现,但用户的问题可能集中在本身,暂时不需要深入继承部分。 总结下来,结构应该先介绍的基本结构,然后逐步展开各部分,每个部分配例子。模板部分同样,从函数模板类模板,再到高级特性。确保语言简洁,例子易懂,覆盖主要知识点。最后可以总结一下这些知识点的应用场景,帮助用户理解其重要性。</think>以下是对C++模板知识点的系统讲解(含实例): --- ### 一、的核心概念 1. **定义** ```cpp class Student { private: // 访问控制 string name; int age; public: // 构造函数 Student(string n, int a) : name(n), age(a) {} // 成员函数 void display() { cout << name << ", " << age << " years old" << endl; } }; ``` 2. **构造函数/析构函数** ```cpp class FileHandler { public: FileHandler(const char* filename) { file = fopen(filename, "r"); // 资源获取 } ~FileHandler() { if(file) fclose(file); // 资源释放 } private: FILE* file; }; ``` 3. **静态成员** ```cpp class Counter { public: static int count; // 声明静态成员 Counter() { ++count; } ~Counter() { --count; } static int getCount() { return count; } // 静态函数 }; int Counter::count = 0; // 定义并初始化 ``` 4. **友元函数/** ```cpp class Box { private: double width; public: friend void printWidth(Box b); // 友元函数 friend class BoxFactory; // 友元 }; void printWidth(Box b) { cout << b.width; // 访问私有成员 } ``` --- ### 二、模板编程 1. **函数模板** ```cpp template <typename T> T max(T a, T b) { return (a > b) ? a : b; } // 使用 cout << max<int>(3, 5); // 显式指定型 cout << max(3.14, 2.71); // 自动型推导 ``` 2. **类模板** ```cpp template <class T, int size=10> // 带默认参数的模板 class Array { private: T arr[size]; public: T& operator[](int index) { return arr[index]; } }; // 实例化 Array<double> floatArr; // 大小使用默认值10 Array<int, 20> intArr; // 显式指定大小 ``` 3. **模板特化** ```cpp // 通用模板 template <typename T> class Printer { public: void print(T val) { cout << "Generic: " << val << endl; } }; // 特化版本(针对char*) template <> class Printer<char*> { public: void print(char* val) { cout << "C-string: " << val << endl; } }; ``` 4. **可变参数模板** ```cpp template<typename... Args> void printAll(Args... args) { (cout << ... << args) << endl; // C++17折叠表达式 } // 调用 printAll(1, " apple", ", ", 3.14, " pi"); ``` --- ### 三、关键特性对比 | 特性 | | 模板 | |----------------|-----------------------------|---------------------------| | 实例化方式 | 编译时确定 | 编译时生成具体型代码 | | 代码生成 | 单次生成 | 按需生成多份代码 | | 型检查 | 严格型匹配 | 延迟到实例化时检查 | | 典型应用场景 | 封装数据行为 | 泛型编程、容器、算法抽象 | --- ### 四、最佳实践 1. 优先使用构造函数初始化列表 2. 模板定义通常放在头文件中 3. 对复杂模板使用`typename`消除歧义 4. 使用`static_assert`进行模板参数约束 ```cpp template <typename T> class Numeric { static_assert(is_arithmetic<T>::value, "Requires numeric type"); }; ``` 这些知识是理解STL容器(如`vector<T>`)、智能指针等高级特性的基础,建议通过实现简单的容器(如自定义`Vector<T>`)来加深理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值