某日二师兄加入XXX科技公司的C++工程师开发岗位第9面:
面试官:C++中,设计一个类要留神哪些货色?
二师兄:设计一个类次要思考以下几个方面:1.面向对象的封装、继承及多态。2.
big three
或者big five
。3.运算符和函数重载、动态成员、友元、异样解决等相干问题。面试官:请介绍一下面向对象的三个个性。
二师兄:封装是将类的函数和数据封装起来,内部不能间接拜访类的数据,而是须要通过办法拜访数据。继承是指一个类能够继承另一个类的属性和办法。多态是指一个对象能够体现出多种状态。
面试官:请问多态是如何实现的?
二师兄:多态的是通过父类的指针或援用指向子类的对象实现的。在对象中保护一个虚指针(
vtptr
),这个指针指向一个虚表(vtable
),当用户通过父类对象拜访子类的办法时,通过查问虚表中对应的办法的地址,并跳转到此地址执行间接拜访对象的办法。所以多态是有一点点运行时开销的。面试官:你方才所说的
big three
和big 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++!(狗头)