C++主题年技巧积累#2——我被static撞了一下腰

简介:
正文:

         一切都源于我在写练习程序时那一瞬间的妄想……
         static关键字?小菜,C#和C++里都有,原理是一样的,会了C#还写不出C++的来?看着!咱这就一样写一个出来!
         先来C#的!
//----------------水之真谛----------------//
//   [url]http://blog.csdn.net/FantasiaX[/url]
//-----------------------------------------------//
using System;
class Student
{
    public static void Report() // C#中成员方法的声明和定义合二为一
    {
        Console.WriteLine("I am a C# student.");
    }
}
class Program
{
    static void Main(string[] args)
    {
        Student.Report(); // OK
    }
}
 
         再来C++的!
#include <iostream>
class Student
{
public:
         static void Report()    // C++成员函数的声明和定义亦可合二为一,但决不是上策!
         {
                   std::cout<<"I am a C++ student."<<std::endl;
         }
};
int main(int argc, char *argv[])
{
         Student::Report(); // OK
         return 0;
}
 
         哈哈!一次编译通过!我说吗,会C#就会C++,没问题的,只是语法结构上稍有不同。
         接下来再来两个静态成员变量的例子!还是先写C#的
//----------------水之真谛----------------//
//   [url]http://blog.csdn.net/FantasiaX[/url]
//-----------------------------------------------//
using System;
class Student
{
    public static string Name; // 猜猜在这里C#都做了些什么?
}
class Program
{
    static void Main(string[] args)
    {
        Student.Name = "Tim";  // OK
        Console.WriteLine(Student.Name);
    }
}
 
         乘胜追击,再来C++的!
#include <iostream>
#include <string>
class Student
{
public:
         static std::string Name;  //猜猜C++在这里做什么了?
};
int main(int argc, char *argv[])
{
         Student::Name = "Tim";  //Error!!!   狂汗~~~ >_<
         std::cout<<Student::Name<<std::endl;
         return 0;
}
         啊~~~竟然有错误!这是怎么回事呢?不应该啊~~~C#里明明能行的~~~
         百思不得其解后,拿出《C++ Primer 第四版(中文)》来查阅……果然找到很多有用信息。
l         P.399 Row4-5.         ……static数据成员独立于该类的任意对象而存在……
l         P.399 Row5-6.         ……每个static数据成员是与类关联的对象,并不与该类的对象关联……
l         P.401 Row1.             ……static数据成员必须在类定义体的外部定义(正好一次)。
注解:数据成员(Data Member)其实就是成员变量。
         上面最有用的信息就是第三条了:原来还需要在类的外部定义一次!于是修改代码为:
#include <iostream>
#include <string>
class Student
{
public:
         static std::string Name;  //猜猜C++在这里做什么了?
};
std::string Student::Name = std::string();  // 类外定义,这句是新添加的。
int main(int argc, char *argv[])
{
         Student::Name = "Tim";  // OK
         std::cout<<Student::Name<<std::endl;
         return 0;
}
 
         问题虽然解决了,但决不能轻易放过这个问题——为什么会这样呢?
 
深挖:

         我把目光移到在《C++ Primer》里找到的前两条消息上。第二条与C#一致,说明不了什么问题,第一条倒是让人眼前一亮——static数据成员独立于该类的任意对象而存在。果真是这样吗?我得自己动手验证一下:
         先写一个类:
class Student
{
public:
         int Age;
};
         然后执行 std::cout<<sizeof(Student)<<std::endl; 得到的结果是4。
         把类改写成这样:
class Student
{
public:
         int Age;
         unsigned int StudentID;
};
         结果是8——预料之中。
         再改写成:
class Student
{
public:
         int Age;
         unsigned int StudentID;
         double Score;
};
         结果是16——可以理解。
         然后加一个static成员试试——
class Student
{
public:
         int Age;
         unsigned int StudentID;
         double Score;
         static int Amount;
};
         结果还是16,没有变!原来static的成员数据真的是独立于类空间之外的!
         问题解决了吗?No!不但没有解决,反而增多了,一个问题变成了两个问题——
1.         类所占的空间本质是什么?
2.         类的static数据成员究竟在哪里?
第一个问题似乎比较好回答:说“类所占的空间”其实欠妥,应该是“类的实例占内存的大小”。
拿上面这个类来说,它的每一个实例中都将包含三个子数据成员(int Age, double Score, double Score),所以会占去16个字节。而这16个字节将会由new操作符在内存中分配出来,并可以在Student类的构造函数去初始化它们——我们没有显式地初始化它们,所以它们的值是一个由上帝掷骰子得出来的值(使用VC执行的时候如果弹出错误警告,请按Ignore,我得到的Age值是-858993460,阴寿乎?)。
问题回答完了吗?没有!
提问:分配了16个字节的内存说明了什么问题?
回答:说明每个非static成员变量都有自己的内存,叠加起来占16字节。
提问:每个非static成员变量都有自己的内存说明了什么问题?
回答:说明为每个变量都分配了内存(哪儿来的砖??!!)
提问:为变量分配了内存说明了什么?
回答:说明每个非static变量不但已经被声明,而且已经被定义了!因为声明变量并不分配内存,只有定义变量的时候才分配内存!
         这才是第一个问题的本质的答案!
 
         第二个问题在《C++ Primer》这本书里就找不到答案了——毕竟是Primer,不是Advanced。于是又祭出宝卷《深入探索C++对象模型》。一番查阅后,在这里找到半个答案:
l         P. 95 Static Data Members节        Static data members,按其字面意义,被编译器提出于class之外……并被视为一个global变量……每一个static data member只有一个实体,存放在程序的data segment之中……
My God! 多么精彩的描述!!第二个问题的答案就是:类的static数据成员会像一个全局(global)变量一样被放在程序的数据段里(谁说大学开的汇编语言课没用来着??),而不占类实例的内存。
         基于此,我们可以推敲出至少两点:
1.         类的static数据成员不占类(的实例)的内存,因此这里只是个声明、没有定义。
2.         “被视为”global变量仅仅是“被视为”,并不像声明并定义了一个真正的global变量。
所以,无论怎么说,我们都欠它一次“定义”——问题彻底解决了!
 
多做之过?What’s under the C#’s hood?

         也许你会问:为了一个小小的static,至于吗?我会坚定地告诉你——至于!不仅仅是因为它让我闪了腰,更因为我们作为程序员,要有严谨的态度和钻研的精神!
         最后收拾一把C#——为什么它的static成员变量就可以直接使用?
         其实上面已经说过,C#的设计理念是把程序员从时时刻刻记着与内存打交道的繁枝缛节中解放出来,因此C#类中的一句public static string Name;不仅仅是一个声明,顺便连定义也做了。
         C#替程序员做的还远不止一个变量的定义。当你试图输出一个没有显式初始化的C#类数据成员(无论是static的还是非static的),都会得到一个默认的“零”值。在MSDN里有张表格——Default Values Table (C# Reference),大家可以参阅。简摘如下:
Value type 
 Default value 
 
bool
 false
 
byte
 0
 
char
 '\0'
 
decimal
 0.0M
 
double
 0.0D
 
enum
 The value produced by the expression (E)0, where E is the enum identifier.
 
float
 0.0F
 
int
 0
 
long
 0L
 
sbyte
 0
 
short
 0
 
struct
 The value produced by setting all value-type fields to their default values and all reference-type fields to null.
 
uint
 0
 
ulong
 0
 
ushort
 0
 









本文转自 水之真谛 51CTO博客,原文链接:http://blog.51cto.com/liutiemeng/18871,如需转载请自行联系原作者
目录
相关文章
|
2月前
|
C++
【c++】static成员
【c++】static成员
【c++】static成员
|
3月前
|
编译器 C++
C++类与对象(四):再谈构造函数(详解初始化列表)、Static成员
C++类与对象(四):再谈构造函数(详解初始化列表)、Static成员
54 0
|
3月前
|
存储 算法 C语言
【C++入门到精通】C++入门 —— 类和对象(初始化列表、Static成员、友元、内部类、匿名对象)
一、初始化列表 ⭕初始化列表概念 ⭕初始化列表的优点 ⭕使用场景 ⭕explicit关键字 二、Static成员 ⭕Static成员概念 🔴静态数据成员: 🔴静态函数成员: ⭕使用静态成员的
69 0
|
9天前
|
C++
【C++成长记】C++入门 | 类和对象(下) |Static成员、 友元
【C++成长记】C++入门 | 类和对象(下) |Static成员、 友元
|
1月前
|
算法 编译器 C++
【C++ 关键字的混合使用 】C++深度探索:auto、static、constexpr的交互影响与应用
【C++ 关键字的混合使用 】C++深度探索:auto、static、constexpr的交互影响与应用
31 0
|
1月前
|
编译器
C++11 静态断言(static_assert)的介绍:介绍静态断言(static assert)在C++11 中的作用和使用方法
C++11 静态断言(static_assert)的介绍:介绍静态断言(static assert)在C++11 中的作用和使用方法
14 0
|
1月前
|
存储 算法 C++
【C/C++ 关键字 存储类说明符】 深入理解C/C++中的static关键字:它的作用、限制和使用技巧
【C/C++ 关键字 存储类说明符】 深入理解C/C++中的static关键字:它的作用、限制和使用技巧
16 0
|
1月前
|
存储 C语言 C++
C/C++静态static属性/用法
C/C++静态static属性/用法
10 0
|
3月前
|
C++
C++——类和对象(初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类)
C++——类和对象(初始化列表、匿名对象、static成员、类的隐式类型转换和explicit关键字、内部类)
|
4月前
|
存储 设计模式 算法
[C++] static静态成员变量/函数的用法
[C++] static静态成员变量/函数的用法
32 1