转载来自:http://blog.csdn.net/cadcisdhht/article/category/785138
总结:
- static的作用是表示该函数只作用在类型的静态变量上,与类的实例没有关系;而const的作用是确保函数不能修改类的实例的状态,与类型的静态变量没有关系。因此不能同时用它们。
- 空类实例不包含信息,本来求sizeof应该是0。但当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。至于占用多少内存,由编译器决定。Visual Studio 2008中每个空类型的实例占用一个byte的空间。由于构造函数和析构函数的调用与类型的实例无关(调用它们只需要知道函数地址即可),在它的实例中不需要增加任何信息。C++的编译器一旦发现一个类型中有虚拟函数,就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针。在32位的机器上,一个指针占4个字节的空间。
- 每个非静态类成员函数都隐藏一个指向自己实例的this指针。
- 调用虚函数需要从一个实例中指向虚函数表的指针以得到函数的地址,因此调用虚函数需要一个实例。调用静态成员函数不要实例。
- &(pPoint->z)的语意是求pPoint中变量z的地址(pPoint的地址0加z的偏移量8),并不需要访问pPoint指向的内存。
- 如果没有标明函数或者变量是的访问权限级别,在struct中,是public的;而在class中,是private的。
- 在C++中,成员变量的初始化顺序与变量在类型中的申明顺序相同,而与它们在构造函数的初始化列表中的顺序无关。
- 如果允许复制构造函数传值,那么会形成永无休止的递归并造成栈溢出,编译错误。因此C++的标准不允许复制构造函数传值参数,而必须是传引用或者常量引用。
- sizeof(数组)是求数组的大小。 sizeof(指针)是求指针的大小,在32位机器上,任意指针都占4个字节的空间。当数组作为函数的参数进行传递时,数组就自动退化为同类型的指针。
- 缺省参数的值是在编译的时候,但确定引用、指针的虚函数调用哪个类型的函数是在运行的时候。
- char p[] = "Hello World";p是一个数组,会开辟一块内存,并拷贝"Hello World"初始化该数组。
题目:
题目(一):C++中我们可以用static修饰一个类的成员函数,也可以用const修饰类的成员函数(写在函数的最后表示不能修改成员变量,不是指写在前面表示返回值为常量)。请问:能不能同时用static和const修饰类的成员函数?
分析:答案是不可以。C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数const this*。但当一个成员为static的时候,该函数是没有this指针的。也就是说此时static的用法和static是冲突的。
我们也可以这样理解:两者的语意是矛盾的。static的作用是表示该函数只作用在类型的静态变量上,与类的实例没有关系;而const的作用是确保函数不能修改类的实例的状态,与类型的静态变量没有关系。因此不能同时用它们。
题目(二):运行下面C++代码,输出是什么?
- class A
- {
- };
- class B
- {
- public:
- B() {}
- ~B() {}
- };
- class C
- {
- public:
- C() {}
- virtual ~C() {}
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- printf("%d, %d, %d/n", sizeof(A), sizeof(B), sizeof(C));
- return 0;
- }
分析:答案是1, 1, 4。class A是一个空类型,它的实例不包含任何信息,本来求sizeof应该是0。但当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。至于占用多少内存,由编译器决定。Visual Studio 2008中每个空类型的实例占用一个byte的空间。
class B在class A的基础上添加了构造函数和析构函数。由于构造函数和析构函数的调用与类型的实例无关(调用它们只需要知道函数地址即可),在它的实例中不需要增加任何信息。所以sizeof(B)和sizeof(A)一样,在Visual Studio 2008中都是1。
class C在class B的基础上把析构函数标注为虚拟函数。C++的编译器一旦发现一个类型中有虚拟函数,就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针。在32位的机器上,一个指针占4个字节的空间,因此sizeof(C)是4。
题目(三):运行下面的C++代码,得到的结果是什么?
分析:答案是Print1调用正常,打印出hello world,但运行至Print2时,程序崩溃。调用Print1时,并不需要pA的地址,因为Print1的函数地址是固定的。编译器会给Print1传入一个this指针,该指针为NULL,但在Print1中该this指针并没有用到。只要程序运行时没有访问不该访问的内存就不会出错,因此运行正常。在运行print2时,需要this指针才能得到m_value的值。由于此时this指针为NULL,因此程序崩溃了。
题目(四):运行下面的C++代码,得到的结果是什么?
- class A
- {
- private:
- int m_value;
- public:
- A(int value)
- {
- m_value = value;
- }
- void Print1()
- {
- printf("hello world");
- }
- virtual void Print2()
- {
- printf("hello world");
- }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- A* pA = NULL;
- pA->Print1();
- pA->Print2();
- return 0;
- }
分析:答案是Print1调用正常,打印出hello world,但运行至Print2时,程序崩溃。Print1的调用情况和上面的题目一样,不在赘述。由于Print2是虚函数。C++调用虚函数的时候,要根据实例(即this指针指向的实例)中虚函数表指针得到虚函数表,再从虚函数表中找到函数的地址。由于这一步需要访问实例的地址(即this指针),而此时this指针为空指针,因此导致内存访问出错。
题目(五):C++中静态成员函数能不能同时也是虚函数?
- class A
- {
- private:
- int m_value;
- public:
- A(int value)
- {
- m_value = value;
- }
- void Print1()
- {
- printf("hello world");
- }
- void Print2()
- {
- printf("%d", m_value);
- }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- A* pA = NULL;
- pA->Print1();
- pA->Print2();
- return 0;
- }
分析:答案是不能。调用静态成员函数不要实例。但调用虚函数需要从一个实例中指向虚函数表的指针以得到函数的地址,因此调用虚函数需要一个实例。两者相互矛盾。
题目(六):运行下列C++代码,输出什么?
- struct Point3D
- {
- int x;
- int y;
- int z;
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- Point3D* pPoint = NULL;
- int offset = (int)(&(pPoint)->z);
- printf("%d", offset);
- return 0;
- }
答案:输出8。由于在pPoint->z的前面加上了取地址符号,运行到此时的时候,会在pPoint的指针地址上加z在类型Point3D中的偏移量8。由于pPoint的地址是0,因此最终offset的值是8。
&(pPoint->z)的语意是求pPoint中变量z的地址(pPoint的地址0加z的偏移量8),并不需要访问pPoint指向的内存。只要不访问非法的内存,程序就不会出错。
题目(七):运行下列C++代码,输出什么?
- class A
- {
- public:
- A()
- {
- Print();
- }
- virtual void Print()
- {
- printf("A is constructed./n");
- }
- };
- class B: public A
- {
- public:
- B()
- {
- Print();
- }
- virtual void Print()
- {
- printf("B is constructed./n");
- }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- A* pA = new B();
- delete pA;
- return 0;
- }
答案:先后打印出两行:A is constructed. B is constructed. 调用B的构造函数时,先会调用B的基类及A的构造函数。然后在A的构造函数里调用Print。由于此时实例的类型B的部分还没有构造好,本质上它只是A的一个实例,他的虚函数表指针指向的是类型A的虚函数表。因此此时调用的Print是A::Print,而不是B::Print。接着调用类型B的构造函数,并调用Print。此时已经开始构造B,因此此时调用的Print是B::Print。
同样是调用虚拟函数Print,我们发现在类型A的构造函数中,调用的是A::Print,在B的构造函数中,调用的是B::Print。因此虚函数在构造函数中,已经失去了虚函数的动态绑定特性。
题目(八):在C++中,struct和class有什么不同?
答案:在C++中,如果没有标明函数或者变量是的访问权限级别,在struct中,是public的;而在class中,是private的。
题目(九):运行下图中的C++代码,输出是什么?
- #include
- class A
- {
- private:
- int n1;
- int n2;
- public:
- A(): n2(0), n1(n2 + 2)
- {
- }
- void Print()
- {
- std::cout
关注打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?


微信扫码登录