当前位置:   article > 正文

c++基础知识汇总(三)计算机与编译原理 | static与const | 内联与虚函数 | sizeof_类中可以同时存在同名同参数的虚函数和静态函数

类中可以同时存在同名同参数的虚函数和静态函数

目录

一、CPU的总线

1.1 32位与64位

1.2 总线类型

1.3 32与64下的内存

二、static与const

2.1 什么是static?

2.2 const

2.3 指针与const的关系

四、虚函数|类|父类

4.1 内联

4.2 虚函数

4.3 虚函|内联

4.4 静态函数

4.5 几种存储方式

五、sizeof(int)在什么过程中出结果

5.1 sizeof数据类型

5.2 sizeof 结构体

5.3 sizeof union

5.4 sizeof数组

5.5 sizeof指针

5.6 sizeof 函数


一、CPU的总线

1.1 32位与64位

32位CPU与64位CPU指的是什么?

指的数据总线。

https://www.cnblogs.com/wtch519361/p/5278343.html

64位数据总线一次就能取出64bit的数据,8位数据总线的CPU一次只能取出8bit的数据

CPU总线,是PC系统中最快的总线,也是芯片组与主板的核心。这条总线主要由CPU使用,用来与高速缓存、主存和北桥(或MCH)之间传送信息。

来自 <https://baike.baidu.com/item/CPU%E6%80%BB%E7%BA%BF/15739624?fr=aladdin>

1.2 总线类型

数据总线DB(Data Bus)、地址总线AB(Address Bus)和控制总线CB(Control Bus),也统称为系统总线,即通常意义上所说的总线。

  • 数据总线(Data Bus):在CPU与RAM之间来回传送需要处理或是需要储存的数据。
  • 地址总线(Address Bus):用来指定在RAM(Random Access Memory)之中储存的数据的地址。
  • 控制总线(Control Bus):将微处理器控制单元(Control Unit)的信号,传送到周边设备。
  • 扩展总线(Expansion Bus):外部设备和计算机主机进行数据通信的总线,例如ISA总线,PCI总线。
  • 局部总线(Local Bus):取代更高速数据传输的扩展总线。

来自 <https://baike.baidu.com/item/%E6%80%BB%E7%BA%BF/108823>

1.3 32与64下的内存

除了long和void之外

Int,double,float的内存占用没有变化

具体内容:

http://www.unix.org/whitepapers/64bit.html

二、static与const

2.1 什么是static?

静态变量(Static Variable)在计算机编程领域指在程序执行前系统就为之静态分配(也即在运行时中不再改变分配情况)存储空间的一类变量。

存储类名

生命周期

作用域

extern

静态(程序结束后释放)

外部(整个程序)

static

静态(程序结束后释放)

内部(仅翻译单元,一般指单个源文件)

auto,register

函数调用(调用结束后释放)

定义全局变量前,加上关键字static,该变量就被定义成了一个静态全局变量.

特点:

  • 该变量在全局数据区分配内存.
  • 初始化:如果不是显示初始化,那么将被隐式初始化为0.
  • 访变量只在本文件可见,即应该为定义之处开始到本文件结束.

程序在内存中一般分为四个区域:

  • 代码区
  • 全局数据区
  • 堆区
  • 栈区

一般程序由new产生的动态数据放在堆区,函数内部的自动变量放在栈区.自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静态局部变量)都存放在全局数据区.因此它们并不会随着函数的退出而释放空间.

 static int n;//定义静态全局变量

改为:  int n;//定义全局变量

相关问题与理解

在C语言中,关于静态变量的说法,正确的是( )。

  • 静态变量和常量的作用相同(错误,静态变量表示内存区中固定位置的变量)
  • 函数中的静态变量,在函数退出后不被释放(正确)
  • 静态变量只可以赋值一次,赋值后则不能改变(错误,静态变量只是内存区中的位置不能改变)
  • 静态全局变量的作用域为一个程序的所有源文件(错误,只是当前程序文件,想要扩展带所有需要加extern修饰)

解析:

正确答案选第二个,静态变量在函数退出后不会被释放。因为静态变量是程序执行之前系统就静态分配的变量了。

作用域为当前文件,从定义/声明位置到文件结尾。动态全局变量可以通过extern关键字在外部文件中使用,但静态全局变量不可以在外部文件中使用。静态全局变量相当于限制了动态全局变量的作用域。

静态变量并不是说其就不能改变值,不能改变值的量叫常量。 其拥有的值是可变的 ,而且它会保持最新的值。说其静态,是因为它不会随着函数的调用和退出而发生变化。即上次调用函数的时候,如果我们给静态变量赋予某个值的话,下次函数调用时,这个值保持不变。

static在类内初始化还是类外初始化?

在C++中,类的静态成员(static member)必须在类内声明,在类外初始化,像下面这样。

  1. class A
  2. {
  3. private:
  4. static int count; // 类内声明
  5. };

为什么?因为静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。

2.2 const

const是一个C语言(ANSI C)的关键字,具有着举足轻重的地位。它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一定帮助。

加了const修饰必须在一开始就初始化值,下面代码编译时候就会报错,因为没有定义时就初始化值;

  1. const int a;
  2. a = 10;

下面代码编译的时候就会报错,因为const的值被更改了。
 

  1. const int a=10;
  2. cin >> a;

2.3 指针与const的关系

1)指针指向的变量的值不能变,指向可变

  1. int x = 1;
  2. int y = 2;
  3. const int* px = &x;
  4. int const* px = &x; //这两句表达式一样效果
  5. px = &y; //正确,允许改变指向
  6. *px = 3; //错误,不允许改变指针指向的变量的值

2)指针指向的变量的值可以改变,指向不可变

  1. int x = 1;
  2. int y = 2;
  3. int* const px = &x;
  4. px = &y; //错误,不允许改变指针指向
  5. *px = 3; //正确,允许改变指针指向的变量的值

3)指针指向的变量的值不可变,指向不可变

  1. int x = 1;
  2. int y = 2;
  3. const int* const px = &x;
  4. int const* const px = &x;
  5. px = &y; //错误,不允许改变指针指向
  6. *px = 3; //错误,不允许改变指针指向的变量的值

四、虚函数|类|父类

 以下描述正确的是( )?

  • 虚函数是可以内联的,可以减少函数调用的开销,提高效率(错误,指针调用的时候,类不确定,所以虚函数不内联)
  • 类里面可以同时存在函数名和参数都一样的虚函数和静态函数(静态函数文件之外的函数可以与静态函数重名,但是文件只能的函数不能与静态函数重名,调用时候无法调用,存在二义性)
  • 父类的析构函数是非虚的,但是子类的析构函数是虚的,delete子类对象指针会调用父类的析构函数
  • 选项都不对

解析:正确选项为C,删除子类后,会调用子类的析构函数,子类的析构函数是虚函数,因此往上寻找父类,调用了父类的析构函数。

4.1 内联

计算机科学中,内联函数(有时称作在线函数编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展(有时称作在线扩展)。

inline?Inline function。即可以理解为编译器直接讲函数编译到程序之中,而不是去调用。

4.2 虚函数

https://baike.baidu.com/item/%E8%99%9A%E5%87%BD%E6%95%B0/2912832?fr=aladdin

通过基类中virtual修饰的函数。在派生类中重新定义的成员函数。

用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。

虚函数具有多态性。以不同的策略实现不同的方法。

简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。

虚函数实例
 

  1. class A
  2. {
  3. public:
  4. void print()
  5. {
  6. cout << "This is A" << endl;
  7. }
  8. };
  9. class B : public A
  10. {
  11. public:
  12. void print()
  13. {
  14. cout << "This is B" << endl;
  15. }
  16. };
  17. int main()
  18. {
  19. A a;
  20. B b;
  21. A *ptr_a = &a;
  22. B *ptr_b = &b;
  23. a.print();
  24. b.print();
  25. ptr_a->print();
  26. ptr_b->print();
  27. int end; cin >> end;
  28. return 0;
  29. }

输出:

  1. This is A
  2. This is B
  3. This is A
  4. This is B

如果将main函数改为下面这样:

  1. int main()
  2. {
  3. //main2
  4. A a;
  5. B b;
  6. A *p1 = &a;
  7. A *p2 = &b;
  8. p1->print();
  9. p2->print();
  10. a.print();
  11. b.print();
  12. int end; cin >> end;
  13. return 0;
  14. }

输出就为

  1. This is A
  2. This is A
  3. This is A
  4. This is B

为什么会这样?直接用类就会调用当前类内的函数,用指针就调用父类的函数。

使用类的引用或指针调用虚函数时,无论虚函数什么时间被调用,它都不能是内联的(因为虚函数的调用在运行时确定,这样就会调用父类);

但是,使用类的对象调用虚函数时,无论虚函数什么时间被调用,它都可以是内联的(因为在编译时编译器知道对象的确定类型)

为了避免这个问题,所以就实现了虚函数,在每次继承中都需要重新初始化。

  1. class A
  2. {
  3. public:
  4. virtual void print(){cout<<"This is A"<<endl;}
  5. };
  6. class B : public A
  7. {
  8. public:
  9. void print(){cout<<"This is B"<<endl;}
  10. };

这样无论如何都能输出:

  1. This is A
  2. This is B
  3. This is A
  4. This is B

解析:基类中函数定义为虚函数,则派生类中函数自动定义为虚函数,每次派生类中都需要重新定义。

4.3 虚函|内联

https://blog.csdn.net/shuangshuang37278752/article/details/38875351

虚函数用于运行时多态或晚绑定或动态绑定,内联函数用于提高程序的效率,内联函数的思想:无论内联函数什么时间被调用,在编译时,内联函数的代码段都可以在被替换或插入,当一些小函数在程序中被频繁使用和调用时,内联函数变得十分有用。

类内部定义的所有函数(虚函数除外)都被隐式或自动认为是内联的

使用类的引用或指针调用虚函数时,无论虚函数什么时间被调用,它都不能是内联的(因为虚函数的调用在运行时确定);

但是,使用类的对象调用虚函数时,无论虚函数什么时间被调用,它都可以是内联的(因为在编译时编译器知道对象的确定类型)

所以,虚函数不同条件下是否内联不确定,但是是可以内联的。类内调用可以内联。

4.4 静态函数

https://baike.baidu.com/item/%E9%9D%99%E6%80%81%E5%87%BD%E6%95%B0/5644260

全局函数:在类外声明static函数的话,相当于一个全局函数,由于由于 static 的限制,它只能在文件所在的编译单位内使用,不能在其它编译单位内使用。

静态函数:需要出现在类中,函数调用的结果不会访问或者修改任何对象(非static)数据成员,这样的成员声明为静态成员函数比较好。它为该类的公用变量,在第一次使用时被初始化,对于该类的所有对象来说,static成员变量只有一份。

静态函数特点:

<1> 其他文件中可以定义相同名字的函数,不会发生冲突

<2> 静态函数不能被其他文件所用。

内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件。 使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。

4.5 几种存储方式

存储说明符auto,register,extern,static,对应两种存储期:

自动存储期和静态存储期。 auto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。

关键字extern和static用来说明具有静态存储期的变量和函数。用static声明的局部变量具有静态存储持续期(static storage duration),或静态范围(static extent)。虽然他的值在函数调用之间保持有效,但是其名字的可视性仍限制在其局部域内。静态局部对象在程序执行到该对象的声明处时被首次初始化。

五、sizeof(int)在什么过程中出结果

编译的阶段,编译,链接,运行?

它在编译时候的运算符号.在Pascal 语言与C语言中,对 sizeof() 的处理都是在编译阶段进行。

解析:

https://www.cnblogs.com/huolong-blog/p/7587711.html

5.1 sizeof数据类型

short、int、long、float、double,不同操作平台上结果不一样

5.2 sizeof 结构体

结构体内需要字节对齐,以加快计算机的取数速度.所以,结构体以填充字节的形式来实现字节对齐.保证让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被4整除的地址上.  空结构体(不含数据成员)的sizeof值为1

1)  结构体变量的首地址能够被其最宽基本类型成员的大小所整除。

2)  结构体的每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要,编译器会在成员之间加上填充字节(internal adding)。

3)  结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员后加上填充字节(trailing padding)。
 

  1. struct S1
  2. {
  3. char a;
  4. int b;
  5. };
  6. sizeof(S1); //值为8,字节对齐,在char之后会填充3个字节。
  7. struct S2
  8. {
  9. int b;
  10. char a;
  11. };
  12. sizeof(S2); //值为8,字节对齐,在char之后会填充3个字节。
  13. struct S3
  14. {
  15. };
  16. sizeof(S3); //值为1,空结构体也占内存

5.3 sizeof union

是union中的内存的最大值。共用体和结构体都是由多个不同的数据类型成员组成, 但在任何同一时刻, 共用体只存放了一个被选中的成员。

例如:

  1. union u
  2. {
  3. int a;
  4. float b;
  5. double c;
  6. char d;
  7. };

5.4 sizeof数组

返回数组的大小。

如果数组是char a[10],这种,sizeof返回10

但是如果是 char a[]="123",需要把结尾的\0 考虑上,返回值是4

对于函数的形参,则返回指针的大小。例如:

  1. void func(char a[3])
  2. {
  3. int c = sizeof(a); //c = 4,因为这里a不在是数组类型,而是指针,相当于char *a。
  4. }
  5. void funcN(char b[])
  6. {
  7. int cN = sizeof(b); //cN = 4,理由同上。
  8. }
  9. sizeof(u); //值为8

5.5 sizeof指针

指针为地址总线的宽度。比如32位机中为4, 64位机中,还是4吗?

  1. char *b = "helloworld";
  2. char *c[10];
  3. double *d;
  4. int **e;
  5. void(*pf)();
  6. cout << "char *b = /"helloworld / " " << sizeof(b) << endl;//指针指向字符串,值为4
  7. cout << "char *b " << sizeof(*b) << endl; //指针指向字符,值为1
  8. cout << "double *d " << sizeof(d) << endl;//指针,值为4
  9. cout << "double *d " << sizeof(*d) << endl;//指针指向浮点数,值为8
  10. cout << "int **e " << sizeof(e) << endl;//指针指向指针,值为4
  11. cout << "char *c[10] " << sizeof(c) << endl;//指针数组,值为40
  12. cout << "void (*pf)(); " << sizeof(pf) << endl;//函数指针,值为4

5.6 sizeof 函数

相当于求函数的返回值,不能求void函数的值。且函数不会被调用。

  1. float FuncP(int a, float b)
  2. {
  3. return a + b;
  4. }
  5. int FuncNP()
  6. {
  7. return 3;
  8. }
  9. void Func()
  10. {
  11. return;
  12. }
  13. int main()
  14. {
  15. cout << sizeof(FuncP(3, 0.4)) << endl; //OK,值为4,sizeof(FuncP(3,0.4))相当于sizeof(float)
  16. cout << sizeof(FuncNP()) << endl; //OK,值为4,sizeof(FuncNP())相当于sizeof(int)
  17. /*cout<<sizeof(Func())<<endl; //error,sizeof不能对返回值为空类型的函数求值*/
  18. /*cout<<sizeof(FuncNP)<<endl; //error,sizeof不能对函数名求值*/
  19. int end; cin >> end;
  20. return 0;
  21. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/731428
推荐阅读
相关标签
  

闽ICP备14008679号