技术面试(C++)

本文是作者对C++技术面试中遇到的语法问题的总结,包括虚函数、纯虚函数、new操作的过程、override与overwrite的区别、static与extern的使用、深拷贝与浅拷贝的概念,以及friend class和friend function的作用,还讨论了虚拟析构函数和堆栈内存管理。通过实例解析,帮助读者深入理解C++的关键语法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面试中也曾经遇到过一些语法类的问题,大部分能够解决,但是也有很多做不出来,今天开始进行C++语法的总结,尤其因为我完全不用Java,所以,C++语法就显得愈加重要,开始阶段可能是一些我已经掌握的表面的知识,之后就是一些比较深层次或者偏门的知识,希望能一天一道,20天左右总结一遍。。


另外,如果各位看到我blog的同学或者前辈觉得我写的有啥错误,也请不吝赐教,因为我C++用的时间并不长,很多东西理解的并不好。。


NO.1 虚函数(virtual function)

这个应该算是我平时可以掌握的知识,因为自己写代码的时候也可能会涉及,就举一个最简单的例子吧,类定义如下所示:

#define NAME_SIZE 50

class Person {
        int id;     // all members are private by default
        char name[NAME_SIZE];
        
    public:
        void aboutMe() {
            cout << "I am a person.";
        }
};

class Student: public Person {
    public:
        void aboutMe() {
            cout << "I am a student.";
        }
};

如果运行:

    Student* p = new Student();
    p->aboutMe();

输出的是"I am a Student",但是,如果运行:

    Person* p = new Student();
    p->aboutMe();

输出的是"I am a Person",这是因为aboutMe()这个函数是在编译的时候解析的,这种机制是static - binding

如果把aboutMe()写作虚函数,则机制是dynamic - binding,此时输出的是Student class的aboutMe(),即输出"I am a student.",代码如下:

#define NAME_SIZE 50

class Person {
        int id;     // all members are private by default
        char name[NAME_SIZE];
        
    public:
        virtual void aboutMe() {
            cout << "I am a person.";
        }
};

class Student: public Person {
    public:
        virtual void aboutMe() {
            cout << "I am a student.";
        }
};

还有一个概念就是虚函数表(vtable),因为这个确实没有学过,所以不深究,但是也不能一问三不知,所以大概说一下vtable的功能:

如果一个类中的某一个函数被声明为虚函数,则就会建立虚函数表用来保存该类的虚函数的地址。同时编译器也会指定一个隐含的vptr来指向这个vtable。

NO.2 纯虚函数(pure virtual function)


NO.3 New的过程

这道题是我朋友面Akuna Capital的一道电面题,如果是我的话,我的答案如下:

int* p = new int[5];

首先获得需要分配空间的大小,之后new操作符会在Heap中分配这么大的内存,然后返回内存的守地址,保存在栈中。

当然,这题我也不确定是不是面试官要的答案,听朋友讲他说面试官对他的答案不是特别满意。。

NO.4 Override & Overwrite

其实这个问题我现在的理解依然相当模糊,很难说出有啥特别的不同来。。。这个问题将来还得不断的去加深理解

先上程序:

#include <iostream>

using namespace std;

class CBase {
    public:
        virtual void A(int val) {
            cout << "CBase::A()" << endl;
        }
        
        void B(int val) {
            cout << "CBase::B()" << endl;
        }
        
        void C(int val) {
            cout << "CBase::C()" << endl;
        }
};

class CDrive: public CBase {
    public:
        virtual void A(int val) {
            cout << "CDrive::A()" << endl;      // override
        }
        
        void B(int val) {
            cout << "CDrive::B()" << endl;      // overwirte
        }
        
        void C(int val) {
            cout << "CDrive::C()" << endl;      // overwrite
        }
};

int main(int argc, char** argv) {
    CDrive child;
    CBase* father = new CDrive();
    child.A(1);
    father->A(1);
    cout << endl;
    
    child.B(1);
    father->B(1);
    cout << endl;
    
    child.C(1);
    father->C(1);
    
    return 0;
}
这个程序大概说明了规则:Override是同名同参虚函数,overwrite则是同名同参函数或者同名非同参的函数及虚函数。输出结果如下图所示:

CDrive::A()
CDrive::A()

CDrive::B()
CBase::B()

CDrive::C()
CBase::C()

这个题还不算完,果断时间看看书还需要继续改进。

NO.5 static & extern 


NO.6 deep copy & shallow copy

这个题我同学面JDSU时曾经遇到过,而且在Cracking the Coding Interview这本书里面也有讲解。。。在自己写类的拷贝构造函数的时候是需要注意的。。

下面的就是一个我写的shallow copy constructor的例子,并不难理解:

// shallow copy constructor
shallow_copy(const shallow_copy& item) {
    a = itme.a;
}

浅拷贝就是在a2 = a1后,他俩指向同一片空间,所以当a1释放后,a2就是指向空,下面举一个deep copy constuctor的例子:

// deep copy constructor
deep_copy(const deep_copy& item) {
    a = new char[strlen(item.a)+1];
    strcpy(a, item.a);
}

NO.7 Friend class & Friend function


NO.8 Virtual Destructor

本题在我去SevOne面试的时候考过。

#include <iostream>

using namespace std;

class Base {
    public:
        Base() {
            cout << "Base construct" << endl;
        }
        ~Base() {
            cout << "Base destruct" << endl;
        }
};

class Derive: public Base {
    public:
        Derive() {
            cout << "Derive construct" << endl;
        }
        ~Derive() {
            cout << "Derive destruct" << endl;
        }
};

int main(int argc, char** argv) {
    Base* b = new Derive();
    delete b;
}
如果程序按上面的写法的话,运行结果为:

Base construct
Derive construct
Base destruct
从这个就可以看出调用的是Base指针指向的destructor,然而Derive的空间并没有得到释放。。

所以此时应该在Base的析构函数前加上virtual,则变为:

#include <iostream>

using namespace std;

class Base {
    public:
        Base() {
            cout << "Base construct" << endl;
        }
        virtual ~Base() {
            cout << "Base destruct" << endl;
        }
};

class Derive: public Base {
    public:
        Derive() {
            cout << "Derive construct" << endl;
        }
        ~Derive() {
            cout << "Derive destruct" << endl;
        }
};

int main(int argc, char** argv) {
    Base* b = new Derive();
    delete b;
}
运行结果为:

Base construct
Derive construct
Derive destruct
Base destruct
这就是使用virtual destructor的原因:希望派生类的析构函数被调用

NO.9 Stack & Heap

这个题也是在SevOne被考过,我答得也是一般。。













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值