24个基本的c++面试问题 *

Toptal sourced essential questions that the best C++ developers and engineers can answer. Driven from our community, we encourage experts to submit questions and offer feedback.

现在就雇佣一个顶级的c++开发人员
Toptal logo是顶级自由软件开发人员的专属网络吗, designers, finance experts, 产品经理, 和世界上的项目经理. 顶级公司雇佣Toptal自由职业者来完成他们最重要的项目.

面试问题

1.

两者有区别吗 class and struct?

View answer

类和结构之间的唯一区别是访问修饰符. Struct members are public by default; class members are private. It is good practice to use classes when you need an object that has methods and structs when you have a simple data object.

2.

下面这行代码将打印出什么?为什么?

#include 

Int main(Int argc, char **argv)
{
    std::cout << 25u - 50;
    return 0;
}
View answer

The answer is not -25. 相反,假设32位整数,答案(这将使许多人感到惊讶)是4294967271. Why?

In C++, 如果两个操作数的类型不同, then the operand with the “lower type” will be promoted to the type of the “higher type” operand, using the following type hierarchy (listed here from highest type to lowest type): long double, double, float, 无符号长整型, long int, unsigned int, int (lowest).

所以当两个操作数是,就像我们的例子一样, 25u (unsigned int)和 50 (int), the 50 被提升为一个无符号整数(i.e., 50u).

此外,操作的结果将是操作数的类型. 因此,结果 25u - 50u 它本身也是一个无符号整数吗. 所以结果是 -25 当提升为无符号整数时转换为4294967271.

3.

下面代码中的错误是什么,应该如何纠正?

my_struct_t *栏;
/* ... 做的东西,包括设置条指向一个已定义的my_struct_t对象 ... */
Memset (bar, 0, sizeof(bar));
View answer

最后一个参数 memset should be sizeof(*bar), not sizeof(bar). sizeof(bar) 的大小。 bar (i.e., the pointer itself)而不是指结构的大小 bar.

因此,代码可以通过使用 sizeof(*bar) 作为调用的最后一个参数 memset.

一个犀利的候选人可能会指出使用 *bar 是否会导致解引用错误 bar 还没有分配. 因此,更安全的解决方案是使用 sizeof (my_struct_t). 然而,一个更敏锐的候选人必须知道在这种情况下使用 *bar 在通话范围内是否绝对安全 sizeof, even if bar 还没有初始化,因为 sizeof 是编译时构造吗.

申请加入Toptal的发展网络

并享受可靠、稳定、远程 自由c++开发工作

申请成为自由职业者
4.

What will i and j 在执行下面的代码后等于? 解释你的答案.

int i = 5;
int j = i++;
View answer

以上代码执行后, i 等于6,但是 j will equal 5.

Understanding the reason for this is fundamental to understanding how the unary increment (++)及减量(--)操作符在c++中工作.

当这些运算符 precede 一个变量,变量的值首先被修改和 then 使用修改后的值. 例如,如果我们将上面的代码片段修改为 int j = ++i;, i 会增加到6和 then j 会被设置为修改后的值,所以两者最终都等于6.

然而,当这些操作符 follow 一个变量,该变量的未修改值被使用和 then 它是递增的或递减的. 这就是为什么,在声明中 int j = i++; 在上面的代码片段中, j 第一个设置为未修改的值 i (i.e., 5) and then i 加到6.

5.

Assuming buf 是一个有效的指针,什么是问题在下面的代码? 有什么替代方法可以避免这个问题?

size_t sz = buf->size();
while ( --sz >= 0 )
{
	/*做某事*/
}
View answer

上面代码中的问题是 --sz >= 0 will always 要真实,这样你就永远不会离开 while loop (so you’ll probably end up corrupting memory or causing some sort of memory violation or having some other program failure, 取决于你在循环中做什么).

原因是 --sz >= 0 will always 真正的是那种 sz is size_t. size_t 只是一种基本无符号整数类型的别名吗. 因此,自 sz 是无符号的,可以吗 never 小于零(因此条件永远不可能为真).

One example of an alternative implementation that would avoid this problem would be to instead use a for 循环如下:

for (size_t i = 0; i < sz; i++)
{
	/*做某事*/
}
6.

考虑下面两个用于打印向量的代码片段. 一比一有什么优势吗. the other? Explain.

Option 1:

vector vec;
/* ... .. ... */
For (auto itr = vec.begin(); itr != vec.end(); itr++) {
	itr->print();
}

Option 2:

vector vec;
/* ... .. ... */
For (auto itr = vec.begin(); itr != vec.end(); ++itr) {
	itr->print();
}
View answer

尽管这两种选择将完成完全相同的事情, 从性能的角度来看,第二个选项更好. 这是因为自增后操作符(i.e., itr++)比预增量运算符(i.e., ++itr). The underlying implementation of the post-increment operator makes a copy of the element before incrementing it and then returns the copy.

That said, many compilers will automatically optimize the first option by converting it into the second.

7.

实现一个模板函数 IsDerivedFrom() 将类C和类P作为模板参数. 当类C从类P派生时,它应该返回true,否则返回false.

View answer

这个问题测试你对c++模板的理解. An experienced developer will know that this is already a part of the C++11 std library (std::is_base_of)或c++ boost库的一部分(boost:: is_base_of). Even an interviewee with only passing knowledge should write something similar to this, 最有可能涉及到一个助手类:

template
类IsDerivedFromHelper
{
    class No { };
    class Yes { No no[3]; };
    
    静态是测试(B*);
    static No Test( ... );
public:
    enum { Is = sizeof(Test(static_cast(0))) == sizeof(Yes) };
    
};


template  
bool IsDerivedFrom() {
    return IsDerivedFromHelper::Is;
}
8.

实现模板布尔值 IsSameClass() 将类A和类B作为模板参数. It should compare class A and B and return false when they are different classes and true if they are the same class.

View answer
template 
struct is_same
{
    静态常量bool值= false;
};

template 
struct is_same
{
    静态常量bool值= true;
};


template 
bool IsSameClass() {
    return is_same::value;
}
9.

有可能有一个递归内联函数?

View answer

尽管您可以从内联函数内部调用它, the compiler may not generate inline code since the compiler cannot determine the depth of recursion at compile time. A compiler with a good optimizer can inline recursive calls till some depth fixed at compile-time (say three or five recursive calls), and insert non-recursive calls at compile time for cases when the actual depth gets exceeded at run time.

10.

下面代码的输出是什么?

#include 

class A {
public:
    A() {}
    ~A() {
        throw 42;
    }
};

Int main(Int argc, const char * argv[]) {
    try {
        A a;
        throw 32;
    } catch(int a) {
        std::cout <
View answer

此程序将异常终止. throw 32 会开始展开堆栈并销毁A类吗. 类A析构函数将在异常处理期间抛出另一个异常, 哪个会导致程序崩溃. 这个问题是测试开发人员是否有处理异常的经验.

11.

给你上如下的图书馆课:

类{
public:
    Something() {
        topSecretValue = 42;
    }
    bool somePublicBool;
    int somePublicInt;
    std:: string somePublicString;
private:
    int topSecretValue;
};

实现一个方法来获取任何给定Something*对象的topSecretValue. The method should be cross-platform compatible and not depend on sizeof (int, bool, string).

View answer

创建另一个类,其中Something的所有成员的顺序相同, 但是有额外的返回值的公共方法. 你的副本Something类应该是这样的:

类SomethingReplica {
public:
    int getTopSecretValue() { return topSecretValue; }
    bool somePublicBool;
    int somePublicInt;
    std:: string somePublicString;
private:
    int topSecretValue;
};

然后,要获取值:

Int main(Int argc, const char * argv[]) {
    Something a;
    SomethingReplica* b = reinterpret_cast(&a);
    std::cout << b->getTopSecretValue();
}

在最终产品中避免这样的代码是很重要的, 但在处理遗留代码时,这仍然是一种很好的技术, 因为它可用于从库类中提取中间计算值. (Note: If it turns out that the alignment of the external library is mismatched to your code, 您可以使用#pragma pack来解决这个问题.)

12.

实现一个void函数F,它接受指向两个整数数组的指针(A and B) and a size N as parameters. 然后繁殖 B where B[i] 是一切的产物吗 A[j] where j != i.

For example: If A = {2, 1, 5, 9}, then B would be {45, 90, 18, 10}.

View answer

This problem seems easy at first glance so a careless developer might write something like this:

void F(int* A, int* B, int N) {
    int m = 1;
    for (int i = 0; i < N; ++i) {
        m *= A[i];
    }
    
    for (int i = 0; i < N; ++i) {
        B[i] = m / A[i];
    }
}

这将适用于给定的示例, 但是当你在输入数组a中加入一个0, 程序会因为除以0而崩溃. 正确的答案应该考虑到这种边缘情况,看起来像这样:

void F(int* A, int* B, int N) {
    //设置prod为中性的乘法元素
    int prod = 1;
    
    for (int i = 0; i < N; ++i) {
        //对于元素“i”,将B[i]设置为A[0] * ... * A[i - 1]
        B[i] = prod;
        //与A[i]相乘,将prod设置为A[0] * ... * A[i]
        prod *= A[i];
    }
    
    //重置prod并将其用于正确的元素
    prod = 1;
    
    for (int i = N - 1; i >= 0; --i) {
        //对于元素“i”,将B[i]乘以A[i + 1] * ... * A[N - 1]
        B[i] *= prod;
        //与A[i]相乘,设置prod为A[i] * ... * A[N - 1]
        prod *= A[i];
    }
}

上面给出的解决方案的复杂度为O(N)。. 虽然有更简单的解决方案可用(这些解决方案将忽略需要采取 0 into account), that simplicity has a price of complexity, generally running significantly slower.

13.

什么时候应该使用虚拟继承?

View answer

While it’s ideal to avoid virtual inheritance altogether (you should know how your class is going to be used) having a solid understanding of how virtual inheritance works is still important:

所以当你有一个类(类a)它继承了双亲(B和C), 它们都有一个共同的父母(D类), 如下所示:

#include 

class D {
public:
    void foo() {
        std::cout << "Foooooo" << std::endl;
    }
};


类C: public D {
};

类B: public D {
};

类A: public B, public C {
};

Int main(Int argc, const char * argv[]) {
    A a;
    a.foo();
}

如果在这种情况下不使用虚拟继承, 你会在A班拿到两份D:一份来自B,一份来自C. To fix this you need to change the declarations of classes C and B to be virtual, as follows:

类C:虚拟公共D {
};

类B:虚拟public D {
};
14.

下面代码的输出是什么?

#include 

Int main(Int argc, const char * argv[]) {
    Int a[] = {1,2,3,4,5,6};
    std::cout << (1 + 3)[a] - a[0] + (a + 1)[2];
}
View answer

上面的命令将输出8,因为:

(1+3)[a]等于a[1+3] == 5

a[0] == 1

(a + 1)[2]等于a[3] == 4

这个问题是测试指针算术知识, 以及指针方括号背后的魔力.

While some might argue that this isn’t a valuable question as it appears to only test the capability of reading C constructs, it’s still important for a candidate to be able to work through it mentally; it’s not an answer they’re expected to know off the top of their head, 而是讨论他们得出的结论以及如何得出的结论.

15.

下面代码的输出是什么?

#include 

class Base {
    virtual void method() {std::cout << "from Base" << std::endl;}
public:
    virtual ~Base() {method();}
    void baseMethod() {method();}
};

类A: public Base {
    void method() {std::cout << "from A" << std::endl;}
public:
    ~(){方法();}
};

Int main(void) {
    Base* Base = new A;
    base->baseMethod();
    delete base;
    return 0;
}
View answer

上面的命令将输出:

from A
from A
from Base

这里需要注意的重要事项是类的销毁顺序和方式 Base的方法返回到它自己的实现一次 A 已经被摧毁了.

16.

这个循环要执行多少次? 解释你的答案.

Unsigned char half_limit = 150;

for (unsigned char i = 0; i < 2 * half_limit; ++i)
{
    //做某事;
}
View answer

如果你说300,你 would 是正确的 i 已被宣布为 int. However, since i 被宣布为 unsigned char,正确答案是 这段代码将导致无限循环.

Here’s why:

The expression 2 * half_limit 会被提升到 int (based on c++转换规则),其值为300. However, since i is an unsigned char, 它由一个8位值表示, 达到255之后, 是否会溢出(因此它会返回0),因此循环将永远进行下去.

17.

如何确保c++函数可以被调用为e.g. Void foo(int, int) 但不像其他类型的 Void foo(long, long)?

View answer

Implement foo(int, int)

Void foo(int a, int b) {
// whatever
}

并通过模板删除所有其他的:

template  void foo(T1 a, T2 b) = delete;

Or without the delete keyword:

template  
void f(T arg1, U arg2);

template <>
Void f(int arg1, int arg2)
{
    //...    
}
18.

下面的代码有什么问题?

class A
{
public:
A() {}
~A(){}
};

B类:公共的
{
public:
B():A(){}
~B(){}
};

int main(void)
{
  A* a = new B();
  delete a;
}
View answer

该行为未定义,因为 A的析构函数不是虚函数. From the spec:

( C++11 §5.3.5/3 ) if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.

19.

什么是存储类?

View answer

A class that specifies the life and scope of its variables and functions is called a storage class.

c++中支持以下存储类: auto, static, register, extern, and mutable.

但是请注意,关键字 register was 在c++ 11中已弃用. In C++17, it was 删除并保留以备将来使用.

20.

如何在c++程序中调用C函数?

View answer

Using an extern "C" declaration:


//C code
Void函数(int i)
{
//code
}

输出int(int i)
{
//code
}
//C++ code
extern "C"{
Void函数(int i);
Void print(int i);
}

Void myfunc(int i)
{
   func(i);
   print(i);
}
21.

下面程序的输出是什么?

#include 

struct A
{
    int data[2];

    A(int x, int y): data{x, y} {}
    虚拟void f() {}
};

Int main(Int argc, char **argv)
{
    A a(22, 33);

    Int *arr = (Int *) &a;
    std::cout <
View answer

的实例 struct A 被视为整数值的数组. On 32-bit architectures the output will be 33, and on 64-bit architectures it will be 22. 这是因为存在虚方法 f() in the struct which makes compiler insert a vptr pointer that points to vtable (a table of pointers to virtual functions of class or struct). On 32-bit architectures the vptr takes 4 bytes of the struct instance and the rest is the data array, so arr[2] 表示对数据数组第二个元素的访问,该元素的值为33. 在64位体系结构中,vptr占用8字节 arr[2] 表示对数据数组第一个元素的访问,该数组包含22.

这个问题是测试虚函数内部的知识, 以及c++ 11特有的语法知识, 因为的构造函数 A 使用c++ 11标准的扩展初始化列表.

Compiled with:

g++ question_vptr.CPP -m32 -std=c++
g++ question_vptr.cpp -std=c++11
22.

你可以有一个吗 static const member function? 解释你的答案.

View answer

A const member function is one which isn’t allowed to modify the members of the object for which it is called. A static 成员函数是指不能为特定对象调用的函数.

Thus, the const modifier for a static 成员函数是没有意义的,因为没有对象与调用相关联.

对这个原因的更详细的解释来自C编程语言. 在C语言中,没有类和成员函数,所以所有的函数都是全局的. 成员函数调用被转换为全局函数调用. 考虑这样一个成员函数:

Void foo(int i);

像这样的呼叫:

obj.foo(10);

被翻译成这样:

foo(&obj, 10);

这意味着成员函数 foo 有一个隐藏的第一个参数类型 T*:

void foo(T* const this, int i);

如果成员函数是const, this is of type const * const this:

void foo(const * const this, int i);

静态成员函数没有这样的隐藏参数,所以没有 this pointer to be const or not.

23.

Explain the volatile and mutable keywords.

View answer

The volatile keyword informs the compiler that a variable may change without the compiler knowing it. 变量声明为 volatile 不会被编译器缓存,因此总是从内存中读取.

The mutable 关键字可用于类成员变量. Mutable variables are allowed to change from within const member functions of the class.

24.

c++支持多重继承. 多重继承可能出现的“菱形问题”是什么? Give an example.

View answer

It means that we cannot create hybrid inheritance using multiple and hierarchical inheritance.

让我们考虑一个简单的例子. 一所大学有附属于它的人. 有些是学生,有些是教员,有些是管理人员,等等. So a simple inheritance scheme might have different types of people in different roles, 所有这些都继承自一个共同的“Person”类. Person类可以定义一个抽象 getRole() method which would then be overridden by its subclasses to return the correct role type.

但是如果我们想要模拟助教的角色会发生什么呢?? 通常,助教是 both a grad student and 一名教员. This yields the classic diamond problem of multiple inheritance and the resulting ambiguity regarding the TA’s getRole() method:

(注意上面继承图的菱形,因此得名.)

Which getRole() 实现应该继承TA? 教员或研究生的证言? 简单的答案可能是让TA类覆盖 getRole() 方法并返回名为“TA”的新定义角色。. 但这个答案也是不完美的,因为它会掩盖一个事实,即助教是, in fact, 既是教员也是研究生.

面试不仅仅是棘手的技术问题, 所以这些只是作为一个指南. 并不是每一个值得雇佣的“A”候选人都能回答所有的问题, 回答所有问题也不能保证成为A级考生. 一天结束的时候, 招聘仍然是一门艺术,一门科学,需要大量的工作.

Why Toptal

厌倦了面试候选人? 不知道该问什么才能让你得到一份好工作?

让Toptal为你找到最合适的人.

现在就雇佣一个顶级的c++开发人员

我们的c++开发者专属网络

想找一份c++开发人员的工作?

让Toptal为你找到合适的工作.

申请成为c++开发人员

工作机会从我们的网络

提出面试问题

提交的问题和答案将被审查和编辑, 并可能会或可能不会选择张贴, 由Toptal全权决定, LLC.

*所有字段均为必填项

寻找c++开发人员?

Looking for C++ Developers? 查看Toptal的c++开发人员.

Julie Wetherbee

自由c++开发人员
United StatesToptal Member Since August 21, 2015

Julie has over 20 years of experience building software applications and leading engineering teams for businesses of all sizes. 她是Java方面的专家, JavaScript, C, C++, and Perl, 并且熟悉许多流行的框架. Recently, Julie designed and implemented a large-scale Oracle database sharding solution for Walmart.com.

Show More

Brady Pomerleau

自由c++开发人员
CanadaToptal Member Since 2022年10月25日

布雷迪有三年的软件工程师经验. 他的经验主要是在C/ c++的嵌入式固件方面. 他在CICD方面也有丰富的经验, GitLab, 用于测试自动化和Python的Docker和脚本. 布雷迪在思科公司有网络方面的经验, Apache中的I型管理程序和全栈开发, Flask, Go, Bootstrap.

Show More

Mike Hutton

自由c++开发人员
United StatesToptal Member Since May 27, 2015

Mike is a software architect and developer with over 25 years of experience developing large-scale mission-critical systems. 他目前专注于Java和J2EE开发, c++和C开发, 以及物联网的嵌入式系统. Also, he is an internationally recognized expert in the area of lottery gaming systems. Mike has been delivering solutions employing geographically diverse teams for the past 16 years.

Show More

Toptal连接 Top 3% 世界各地的自由职业人才.

加入Toptal社区.

Learn more