为什么非const静态成员变量一定要在类外定义

2023-10-19 12:15

本文主要是介绍为什么非const静态成员变量一定要在类外定义,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

当我们如下声明了一个类:

class A{public:static int sti_data;// 这个语句在c++11前不能通过编译,在c++11的新标准下,已经能够在声明一个普通变量是就对其进行初始化。int a = 10static const int b = 1;//...其他member
};// 在类外定义静态成员变量并分配内存
int A::sti_data = 10;

上述的类只是声明了而已,并不是在系统中实际存在,要使用一个类,必须在系统中分配内存,也就是实例化出一个对象,比如:

int main () {A a;
}

为了明白为什么类的非const静态变量一定要在类的外边定义:

  • 首先,需要搞清楚,文件中的类A的写法只是声明类A实现形式,在我们没有实例化一个类对象前,系统中不存在分配给类A的内存。同理,我们实例化了N个类A的对象,系统分配了N个A大小的内存。
  • 其次,静态数据成员是一个类共有的,而不是一个对象独有,整个程序中只有一份静态数据成员的内存。
  • 假如,我们不对静态成员变量进行与普通成员变量不同的处理,而是一样能在类的声明里事先定义并初始化好静态成员变量(也就是声明时给定一个初始值,等真正实例化一个对象时便用事先给定的初始值初始化这个变量):static int sti_data = 1;,这意味着,每个我们实例化的类对象,都存在一个分配给静态成员变量的内存,这将导致在内存中存在多个同名的静态变量,这与静态变量的唯一性矛盾,必然编译不通过。
  • 从实现来看,编译器确实能做到,每次实例化一个类对象时判断类中的静态变量是否已经定义过,但很显然,这么做的成本太高,还不如直接强制要求不能在类中对非const静态变量进行定义并初始化,而是只能声明。
  • 至于为什么const静态变量能够在类内直接定义,因为static const 成员变量会被编译器优化,为编译期常量,编译器不会为其分配内存,更像是宏定义那样,在编译期时,在使用它的地方,用它的值替换它,这一点可以通过代码看到,若我们在类中定义一个static const 成员变量,我们可以打印出它的值,却不能打印出它的地址,因为编译器并没有给它分配内存。
  • 因此,c++要求,非const静态变量一定要在类外定义。

为什么(普通或类的)函数里的静态变量能够事先初始化
这个问题主要时为了与第一个问题进行对比,加深理解。
原因很简单,每个的函数,无论是否静态,是否是成员函数还是普通函数,程序在编译时,会给每个函数分配内存,同时一个函数在程序里只有一份内存,这与类在实例化一个对象时才分配内存是有本质上的区别的。这时,对于函数的静态变量而言,只需要把静态变量的内存分配到静态区就能做到在程序的生命周期里不随着函数的调用而被构造或者销毁。

这篇关于为什么非const静态成员变量一定要在类外定义的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/239694

相关文章

MyBatis-Plus中静态工具Db的多种用法及实例分析

《MyBatis-Plus中静态工具Db的多种用法及实例分析》本文将详细讲解MyBatis-Plus中静态工具Db的各种用法,并结合具体案例进行演示和说明,具有很好的参考价值,希望对大家有所帮助,如有... 目录MyBATis-Plus中静态工具Db的多种用法及实例案例背景使用静态工具Db进行数据库操作插入

Apache伪静态(Rewrite).htaccess文件详解与配置技巧

《Apache伪静态(Rewrite).htaccess文件详解与配置技巧》Apache伪静态(Rewrite).htaccess是一个纯文本文件,它里面存放着Apache服务器配置相关的指令,主要的... 一、.htAccess的基本作用.htaccess是一个纯文本文件,它里面存放着Apache服务器

解读静态资源访问static-locations和static-path-pattern

《解读静态资源访问static-locations和static-path-pattern》本文主要介绍了SpringBoot中静态资源的配置和访问方式,包括静态资源的默认前缀、默认地址、目录结构、访... 目录静态资源访问static-locations和static-path-pattern静态资源配置

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

java如何调用kettle设置变量和参数

《java如何调用kettle设置变量和参数》文章简要介绍了如何在Java中调用Kettle,并重点讨论了变量和参数的区别,以及在Java代码中如何正确设置和使用这些变量,避免覆盖Kettle中已设置... 目录Java调用kettle设置变量和参数java代码中变量会覆盖kettle里面设置的变量总结ja

Perl 特殊变量详解

《Perl特殊变量详解》Perl语言中包含了许多特殊变量,这些变量在Perl程序的执行过程中扮演着重要的角色,:本文主要介绍Perl特殊变量,需要的朋友可以参考下... perl 特殊变量Perl 语言中包含了许多特殊变量,这些变量在 Perl 程序的执行过程中扮演着重要的角色。特殊变量通常用于存储程序的

变量与命名

引言         在前两个课时中,我们已经了解了 Python 程序的基本结构,学习了如何正确地使用缩进来组织代码,并且知道了注释的重要性。现在我们将进一步深入到 Python 编程的核心——变量与命名。变量是我们存储数据的主要方式,而合理的命名则有助于提高代码的可读性和可维护性。 变量的概念与使用         在 Python 中,变量是一种用来存储数据值的标识符。创建变量很简单,

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

Thymeleaf:生成静态文件及异常处理java.lang.NoClassDefFoundError: ognl/PropertyAccessor

我们需要引入包: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>sp