某日二师兄加入XXX科技公司的C++工程师开发岗位第9面:

面试官:C++中,设计一个类要留神哪些货色?

二师兄:设计一个类次要思考以下几个方面:1.面向对象的封装、继承及多态。2.big three或者big five。3.运算符和函数重载、动态成员、友元、异样解决等相干问题。

面试官:请介绍一下面向对象的三个个性。

二师兄:封装是将类的函数和数据封装起来,内部不能间接拜访类的数据,而是须要通过办法拜访数据。继承是指一个类能够继承另一个类的属性和办法。多态是指一个对象能够体现出多种状态。

面试官:请问多态是如何实现的?

二师兄:多态的是通过父类的指针或援用指向子类的对象实现的。在对象中保护一个虚指针(vtptr),这个指针指向一个虚表(vtable),当用户通过父类对象拜访子类的办法时,通过查问虚表中对应的办法的地址,并跳转到此地址执行间接拜访对象的办法。所以多态是有一点点运行时开销的。

面试官:你方才所说的big threebig five是什么?

二师兄:(嘿嘿,被装到了)类的big three别离是拷贝构造函数(copy constructor)、拷贝赋值运算符(copy assignment)和析构函数。而类的big five则多了两个,别离是挪动构造函数(move constructor)和挪动赋值运算符(move assignment)。前面两个是C++11之后引入的。

面试官:好的。那你晓得为什么要引入挪动结构和挪动赋值吗?

二师兄:次要是为了效率。挪动结构和挪动赋值不须要把所有的数据从新拷贝一遍,而是霸占了被挪动对象的数据的所有权。代价是被挪动对象在被挪动后不能应用。

面试官:嗯。那你晓得为什么挪动结构和挪动赋值都要加上noexcept关键字吗?

二师兄:额。。。如同不让抛异样?

面试官:你晓得类的动态成员变量须要留神哪些问题吗?

二师兄:要留神哪些问题?额。。。

面试官:在成员办法后加const是为什么?

二师兄:次要是为了束缚这个成员办法不更改对象的任何数据。

面试官:还有其余的起因吗?

二师兄:如同没有了吧。。。

面试官:类的成员办法能够通过const符号重载吗?

二师兄:这个,,应该能够吧。。

面试官:你晓得什么是类的成员办法的援用限定符吗?

二师兄:没有据说过耶。。。

面试官:好的,回去等告诉吧。

让咱们来看一看今日二师兄的体现吧,

为什么挪动结构和挪动赋值都要加上noexcept关键字?

因为在应用挪动语义时,通常会将资源的所有权从一个对象转移到另一个对象,而不是复制资源。如果抛出异样,那么在转移资源的过程中可能会呈现问题,导致资源透露或其余不可预测的行为。

另外,加上 noexcept 关键字还能够进步代码的性能,因为编译器能够在不必要的状况下进行优化。

类的动态成员变量须要留神哪些问题?

动态成员变量的初始化程序是不确定的。如果一个动态成员变量依赖于另一个动态成员变量的值,要确保第二个动态化成员先被初始化,否则程序可能会呈现未定义的行为。

动态成员变量的值能够被多个实例同时批改,因而在多线程拜访动态成员时要留神数据竞争问题。动态变量的生命周期与程序的生命周期雷同,因而它们可能会占用大量的内存。

在成员办法后加const是为什么?

一是能够束缚此办法不会更改对象的任何数据。二是cosnt对象也能够拜访此成员办法。

#include <iostream>struct Foo{    void f1(){std::cout <<"f1" << std::endl;}    void f2() const{std::cout <<"f2" << std::endl;}};int main(int argc, char const *argv[]){    Foo foo;    foo.f1();    foo.f2();    const Foo& foo2 = foo;    foo2.f1();  //这里无奈通过编译,因为const对象无法访问非const 办法    foo2.f2();  //这里能够通过编译}
类的成员办法能够通过const符号重载吗?

这是一个很好的问题,预计很多人没有思考过。先说答案,底层const能够,而顶层const不能够。

#include <iostream>struct Foo{};struct Goo{    void f1(Foo& f){std::cout <<"non const function" << std::endl;}    void f1(const Foo& f){std::cout <<"const function" << std::endl;}};int main(int argc, char const *argv[]){    Foo foo;    Goo goo;    goo.f1(foo);    //无奈通过编译,error: ‘void Goo::f1(Foo)’ cannot be overloaded with ‘void Goo::f1(Foo)’    return 0;}

当咱们把顶层const改为底层const

#include <iostream>struct Foo{};struct Goo{    void f1(Foo& f){std::cout <<"non const function" << std::endl;}    void f1(const Foo& f){std::cout <<"const function" << std::endl;}};int main(int argc, char const *argv[]){    Foo non_const_foo;    const Foo const_foo;    Goo goo;    goo.f1(non_const_foo);    //能够通过编译    non const function    goo.f1(const_foo);    //能够通过编译 const function    return 0;}

那么咱们是否通过在函数括号后加上const来重载函数呢?

#include <iostream>struct Goo{    void f1() {std::cout <<"non const function" << std::endl;}    void f1() const{std::cout <<"const function" << std::endl;}};int main(int argc, char const *argv[]){    Goo non_const_goo;    const Goo const_goo;    non_const_goo.f1();       const_goo.f1();       return 0;}

答案是必定的,因为const_goo.f1() 能够等同于f1(const Goo* goo),也是底层const

最初一个问题尽管简略,但我置信至多有80%的C++程序员不晓得是什么,

什么是类的成员办法的援用限定符吗?

类的成员办法的援用限定符是 C++11 中引入的一种新个性,用于指定成员办法的参数是左值援用还是右值援用。

#include <iostream>struct Foo{    void f1() & {std::cout << "only left reference can call this function" << std::endl;}    void f1() && {std::cout << "only right reference can call this function" << std::endl;}};int main(int argc, char const *argv[]){    Foo foo;    foo.f1();       //left reference    Foo().f1();     //right reference    return 0;}

好了,今日份面试到这里就完结了,小伙伴们,对于明天二师兄的面试,能打几分呢?如果是你,以上的问题都能答复的上来吗?

关注我,带你21天“精通”C++!(狗头)