剑指offer读书总结-->面试所需的基础知识

本篇博文主要总结剑指offer上第二章一些没有说的很清楚的问题。

常被问到的c++基础知识

问题一: 类型转换问题

c++ 中有哪 4 个与类型转换相关的关键字?各有什么特点?在哪些场合下使用?

  • reinterpret_cast
  • static_cast
  • dynamic_cast
  • const_cast

reinterpret_cast :转换一个指针为其它类型的指针;以及指针与足够大 的整数类型之间的转换;从整数类型(包括枚举类型)到指针类型,无视大小。

  1. 对于指针类型转换:这个操作符能够在非相关 的类型之间转换。操作结果只是简单的从一个指针到别的指针的值的二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换。

  2. 对于指针与整数转换:先把一个指针转换成一个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值。

const_cast :用来去除 const 限定,对于 const 变量,我们不能修改它的值,但是有时候我们需要修改它值时该怎么办?这是就可以通过使用 const_cast 来修改 const 变量。

const int constant = 21;
const int* const_p = &constant;
int* modifier = const_cast<int*>(const_p);
*modifier = 7;

static_cast :与 reinterpret_cast 类似,不仅可以用在指针和引用上,还可以用在基础数据和对象上,不同的是需要转换的两个类型具有一定的关系 static_cast 支持指向基类的指针和指向子类的指针之间的互相转换。但是从基类到子类的转换,用 static_cast 并不是安全的。

dynamic_cast :是用来检查两者是否有继承关系。因此该运算符实际上只接受基于类对象的指针和引用的类转换

问题二: sizeOf 问题

定义一个空的类型,里面没有任何成员变量和成员函数,对该类型求 sizeOf ,得到的结果是多少?如果有虚函数结果又如何?看下面测试案例

#include <iostream>
using namespace std;
class a {};
class b {};
class c :public a {//包含虚函数
    virtual void fun() = 0;
};

class d :public b, public c {};

class e {
private:
    int data;
};
class f {
private:
    int data;
    static int data1;
};
int f::data1 = 0;

class g {
private:
    g(int a) {};
    ~g() {};
};

int main()
{
    cout << "sizeof(a):空类=" << sizeof(a) << endl;
    cout << "sizeof(b):空类=" << sizeof(b) << endl;
    cout << "sizeof(c):包含虚函数=" << sizeof(c) << endl;
    cout << "sizeof(d):空类和包含虚函数的类=" << sizeof(d) << endl;
    cout << "sizeof(e):包含一个int数据成员=" << sizeof(d) << endl;
    cout << "sizeof(f):包含一个int数据成员和一个静态变量=" << sizeof(d) << endl;
    cout << "sizeof(g):包含构造函数和析构函数=" << sizeof(d) << endl;
    return  0;
}

程序结果:

sizeof(a):空类=1
sizeof(b):空类=1
sizeof(c):包含虚函数=8
sizeof(d):空类和包含虚函数的类=16
sizeof(e):包含一个int数据成员=4
sizeof(f):包含一个int数据成员和一个静态变量=4
sizeof(g):包含构造函数和析构函数=1

我们可以看到 sizeof(a),sizeof(b) 都为 1 ,空类型的实例不包含任何信息,本来求 sizeOf 结果应该是 0 ,但是当我们声明该类型实例时,他必须在内存中占有一定的空间,否则无法使用这个实例。至于占多大空间,由编译器决定,VS中是1个字节。

如果类中包含构造函数和虚函数和前面一样,还是 1 ,调用构造函数和析构函数,只需要知道函数地址即可,与类型实例无关。

如果类中包含虚函数,则为该类生成虚函数表,每一个实例都会添加指向虚函数表的指针。

类d是由类b,c派生的,它的大小应该为二者之和5,为什么却是8呢?这是因为为了提高实例在内存中的存取效率.类的大小往往被调整到系统的整数倍.并采取就近的法则,里哪个最近的倍数,就是该类的大小,所以类d的大小为8个字节.

类的静态数据成员 被编译器放在程序的一个 global data members 中,它是类的一个数据成员。但是它不影响类的大小,不管这个类实际产生了多少实例,还是派生了多少新的类,静态成员数据在类中永远只有一个实体存在,而类的非静态数据成员只有被实例化的时候,他们才存在。但是类的静态数据成员一旦被声明,无论类是否被实例化,它都已存在。可以这么说,类的静态数据成员是一种特殊的全局变量。

总结:可以看出类的大小与它当中的构造函数,析构函数,以及其他的成员函数无关,只与它当中的成员数据,虚函数有关。

从以上的几个例子不难发现类的大小:

  • 为类的非静态成员数据的类型大小之和。
  • 有编译器额外加入的成员变量的大小,用来支持语言的某些特性(如:指向虚函数的指针)。
  • 为了优化存取效率,进行的边缘调整。
  • 与类中的构造函数,析构函数以及其他的成员函数无关。

问题三:构造函数问题

class A{  
private:  
    int value;  
public:  
    A(int n){ value = n; }  
    A(A other){ value = other.value; }  
    void Print() {cout<<value<<endl; }  
};  
int main(void)  
{  
    A a = 10;  
    A b = a;  
    b.Print();  
    return 0;  
} 

对上面这段代码进行分析编译运行的结果是:
A、编译错误 B、编译成功,运行时程序崩溃 C、编译运行正常,输出10

答案:A、编译错误。复制构造函数 A(A other) 传入的参数是 A 的一个实例。由于是传值参数,我们把形参复制到实参会调用复制构造函数。因此如果允许复制构造函数传值,就会在复制构造函数内调用复制构造函数,就会形成永无休止的递归调用从而导致栈溢出。

上面加黑斜体的部分不是很理解,特意查了下调用复制构造函数的情况:

#include <iostream>
using namespace std;

class A {
public:

    A(int data) { cout << "类A复制构造函数被调用" << endl; };
    A(A& a) { cout << "类A的带对象参构造函数被调用" << endl; };

    void test(A a) {

    }
    A fun(A a)
    {
        return a;
    }
};


int main()
{
    cout << "A a(int 1) "; A a(1); cout << endl;
    cout << "A b = a"; A b = a; cout << endl;
    cout << "A c(int 1)"; A c(1); cout << endl;
    cout << "c.test(A a)"; c.test(b); cout << endl;
    cout << "c.fun(A a)"; c.fun(a); cout << endl;
}

程序运行结果:

A a(int 1) 类A复制构造函数被调用

A b = aA的带对象参构造函数被调用

A c(int 1)类A复制构造函数被调用

c.test(A a)类A的带对象参构造函数被调用

c.fun(A a)类A的带对象参构造函数被调用
类A的带对象参构造函数被调用
  1. 显式或隐式地用同类型的一个对象来初始化另外一个对象;
  2. 作为实参传递给一个函数。
  3. 在函数体内返回一个对象时,也会调用返回值类型的拷贝构造函数。
  4. 初始化序列容器中的元素时。比如 vector<string>svec(5)string 的缺省构造函数和拷贝构造函数都会被调用。
  5. 用列表的方式初始化数组元素时。 stringa[]=string(hello),string(world); 会调用 string 的拷贝构造函数。

由此我们可知:如果复制构造函数 A(A other) 传入的参数是 A 的一个实例 a 时,会有 A other=a 时,就会调用构造函数。这样就会不断的递归调用复制构造函数,从而导致栈溢出。我们可以把构造函数修改为 A(constA&other) 也就是把传值参数改为常量引用。

猜你喜欢

转载自blog.csdn.net/mr_tyting/article/details/78062580