C/C++编程:类模板中的静态成员

1059 篇文章 275 订阅

当类模板中有静态成员变量时,情况与普通类的静态成员变量不同。普通类中的静态成员函数需要在某个代码文件中显式声明,以便在该代码文件编译后可以为静态成员变量留出存储空间以供之后链接使用。而类模板中的静态变量却无法如此处理。

C++标准提倡将模板的所有实现都放在头文件中以便编译器可以当场实现模板实例,这样能够避免产生跨目标链接。但是类模板静态成员变量却与这一提倡冲突。类模板的静态成员变量是所有同类型的类模板实例共享的一块数据。当多个目标文件中声明了同一类模板的同类型实例后,必然会产生跨目标文件链接。为了与标准所倡导的风格一致,C.++编译器都会对类模板静态成员变量做特殊处理。

首先,代码必须按照C++标准的要求,将类模板静态成员变量的实现与类模板实现放在同一可见范围内。通常,将静态成员变量的实现写在类模板实现之后,由于是类模板的成员,其实现也必须写成模板。如下:

template <typename T>
struct the_class{
    static int id;
    the_class() {++ id;}
};

template <typename T>
int the_class<T>::id = 0;

只要静态成员变量的模板与其类模板同时可见,编译器就可针对类模板的静态成员变量做特殊处理:

  • 在目标文件中写入类模板实例中静态成员变量的初始值。
  • 将此模板实例静态成员变量做类似外部变量处理,即在汇编代码中为该变量临时分配一个内存地址,但在目标文件中标记该地址所关联的变量名以及链接属性等,以便在随后又链接器修改地址,以正确实现多个类模板实例共享同一内存地址

在链接时同样需要对类模板静态成员变量做特殊处理。因为类模板静态成员变量的实现以及初始值是写在头文件中,故而在每个包含了该头文件的代码文件中,都会存在若干个该类实例的静态成员变量“副本”。如果在不同文件中都生成了同一模板参数值的实例,则会有多个该实例的“副本”,从而产生冲突。此时,链接器需要解决此冲突。

看个例子:

// call1.cpp
#include <iostream>
#include "the_class.h"

void call1(){
    the_class<int> c;
    std::cout << c.id << "\n";
};
// call2.cpp

#include <iostream>
#include "the_class.h"

void call2(){
    the_class<int> c;
    std::cout << c.id << "\n";
};
// main.cpp
#include <string>
#include <iostream>

void call1();
void call2();

int main(){
    call1();
    call2();
    return 0;
}

在这里插入图片描述
call1.cpp和call2.cpp定义的call1()和call2()两个函数都生成了类模板the_class的实例the_class<int>,编译器会分别在编译两个代码文件所生成的目标文件中,为其静态成员变量the_class<int>::id分配内存地址。而按照类静态成员的概念,所有类实例应该共享同一套静态成员存储空间。在链接时,链接器将随机选择一个目标中的空间作为最终存储空间,从而使不同目标文件中的多个等价目标实例共享同一套静态成员空间。

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
良好的编程规范可以改善软件质量,缩短上市时间,提升团队效率,简化维护工作。在,两位全世界最受尊敬的C++专家将全球C++社区的集体智慧和经验凝结成一整套编程规范。这些规范可以作为每一个开发团队制定实际开发规范的基础,更是每一位C++程序员应该遵循的行事准则。, 涵盖了C++程序设计的每一个方面,包括设计和编码风格、函数、操作符、类的设计、继承、构造与析构、赋值、名字空间、模块、模板、泛型、异常、STL容器和算法等。书对每一条规范都给出了言简意赅的叙述,并辅以实例说明;另外还给出了从类型定义到错误处理等方面的大量C++ 最佳实践,包括许多最新总结和标准化的技术,即使使用C++多年的程序员也会从受益匪浅。, 通过阅读,可以找到以下问题的答案。,  哪些东西值得标准化?哪些东西不值得标准化?,  使代码可扩展的最佳方法是什么?,  合理的错误处理策略有哪些要素?,  如何(和为什么要)避免不必要的初始化、循环依赖和定义依赖?,  何时应该(以及如何)同时使用静态和动态的多态性?,  如何实践“安全的”改写?,  何时该提供不会失败的交换?,  为什么要阻止异常跨越模块边界传播?如何阻止?,  为什么不应该在头文件写名字空间声明或指令?,  为什么应该使用STL vector和string代替数组?,  如何选择正确的STL搜索算法?,  为了保证代码的类型安全,应该遵从哪些规则?
 C++领域20年集大成之作   两位世界专家联袂巨献   适合所有层次C++程序员   良好的编程规范可以改善代码质量,缩短上市时间,提升团队效率,简化维护工作。在本书,两位全世界受尊敬的C++专家将全球C++社区的集体智慧和经验凝结成一整套编程规范。这些规范可以作为每一个开发团队制定实际开发规范的基础,更是每一位C++程序员应该遵循的行事准则。   本书涵盖了C++程序设计的每一个方面,包括设计和编码风格、函数、操作符、类的设计、继承、构造与析构、赋值、名字空间、模块、模板、泛型、异常、STL容器和算法等。书对每一条规范都给出了言简意赅的概述,并辅以实例说明;另外还给出了从类型定义到错误处理等方面的大量C++实践,包括许多总结和标准化的技术。即使使用C++多年的程序员也会从受益匪浅。   通过阅读本书,可以找到以下问题的答案。   哪些东西值得标准化?哪些东西不值得标准化?   使代码可扩展的方法是什么?   合理的错误处理策略有哪些要素?   如何(和为什么要)避免不必要的初始化、循环依赖和定义依赖?   何时应该(以及如何)同时使用静态和动态的多态性;   如何实践“安全的”改写?   何时该提供不会失败的交换?   为什么阻止异常跨越模块边界传播?如何阻止?   为什么不应该在头文件写名字空间声明或指令?   为什么应该使用STL vector和string代替数组?   如何选择正确的STL搜索或排序算法?   为了保证代码的类型安全,应该遵从哪些规则?

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值