【c++面试集】年度整理
系列文章目录
前言
每一年有的企业会对相关专业做一次语言考试,作为个人也应对自己的主要编程语言做一次年度考试。
一、C++基础(必备)
三目运算符表达式
条件表达式 “a?b:c”,当a为真时,取b值,否则取c值。C/C++遵循的规则是“非零即为真”,所有不是零的数,都可以认为是“true”,而仅把零当做false,即“为真”即“不等于零”,“为假”即“等于零”,所以题目中w等价的表达式是w非零。
若有表达式“(w) ? (–x) : (++y)”,则其中与w等价的表达式是: w!=0
原码、反码和补码
正数的原码、反码和补码都相同;负数原码和反码的相互转换的方式是符号位不变,数值位按位取反;负数原码和补码的相互转换的方式是符号位不变,数值位按位取反,末位再加1;0的补码表示只有一种。
下列关于原码、反码和补码的描述,正确的是: ABCD
A、负整数的符号位为1
B、0的补码是唯一的
C、正整数的原码、反码和补码都一样
D、十进制数-122的原码为11111010,反码为10000101,补码为10000110
常量定义
下列哪些写法是对的,而且是常量? ABD
A、125
B、1.25E+20
C、‘AB’
D、-0.456
变量定义
下列代码的main函数中哪个标志符是变量名?
class Base
{
public:
Base() {}
Base(int i)
: counter(i)
{
}
private:
int counter = 0;
};
int main()
{
Base a(); // 函数声明的优先级最高,所以标志符a是函数名
Base b(1); // 变量名
Base c = 1;// 变量名
Base d{};// 变量名
return 0;
}
变量持久性
全局变量持久性跟进程相同;静态全局变量改变的链接性,持久性跟进程相同;静态局部变量的作用域是局部的,但是持久性跟进程相同;静态成员变量的持久性跟进程相同。
以下哪种类型的变量的持久性跟进程是一样的? BCEF
A、非静态成员变量
B、静态全局变量
C、静态局部变量
D、非静态局部变量
E、静态成员变量
F、全局变量
lambda 表达式默认捕获变量
lambda只能捕获非静态局部变量,不能捕获全局变量和静态局部变量;this是一种特殊的局部变量。
下面的代码,lambda表达式捕获了哪几个变量? BD
int g_counter = 0;
class Base
{
public:
void testLambda()
{
int index = 0;
static int counter = 1;
auto callback = [=]() // =代表可以捕获所有,但lambda只能捕获非静态局部变量,不能捕获全局变量和静态局部变量; this是一种特殊的局部变量
{
};
}
};
A、counter
B、this
C、g_counter
D、index
const、virtual、static和noexcept关键字的用法
- const关键字可以用来修饰变量和函数
- virtual关键字只能用来修饰函数和类继承
- static关键字可以用来修饰变量和函数,用来定义静态成员变量和静态成员函数;还可以用来改变全局变量的链接行以及局部变量的持久性;也可以改变全局函数的链接性
- noexcept关键字用来表明该函数不会产生异常,允许编译器尽量优化异常相关的代码
以下哪几个关键字即可用来修饰变量又可以用来修饰函数? AD
A、const
B、virtual
C、noexcept
D、static
自增自减在while中使用
#include <iostream.h>
void main()
{
int num=2,i=6;
do {
i--;
num++;
} while(--i);
cout<<num<<endl; // do-while循环,前缀先使i减少1后判断是否为零,不为零时再次执行循环,为零退出循环。循环值执行3次就退出,所以结果为5。
}
模板使用
模板是类型无关的,具有很高的可复用性;模板是平台无关的,可移植性;模板是编译时检查数据类型而不是运行时,保证了类型安全;模板可用来创建动态增长和减少的数据结构。
C++中使用模板类的原因,下列描述正确的是? ABD
A、可用于基本数据类型
B、可用来创建动态增长和减小的数据结构
C、它是类型相关的,因此具有很高的可复用性。
D、它是平台无关的,可移植性
E、它在运行时而不是编译时检查数据类型,保证了类型安全
类和结构体区别
class 可以使用模板,而 struct 不能。
关于C++中类class和C语言中struct的区别描述,说法正确的是? ACD
A、class中的成员默认是private 属性的,struct的成员默认是public 属性的
B、class 和struct都可以使用模板类型
C、class有继承、多态的特性,struct没有
D、class可以定义成员函数,struct只能定义成员变量
标准库strcpy使用
// 请问运行Test 函数会有什么样的结果?
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”); // world
printf(str);
}
}
sizeof 运算符计算字节数
sizeof计算结构体字节数
// 假设sizeof(int)等于4,则sizeof(struct Derived)的值是什么?
// 编译器默认会在结构体成员变量之间插入补齐字节,确保每一个成员变量都是自然对齐的。
struct Base
{
char a; // 偏移量为0, 0 + 1 = 1
int b; // 偏移量为4,4 +4 = 8
char c; // 偏移量为8, 8 + 1 = 9, 但要自然对齐规则的满足为int(4) 的整数倍,因此添加3个pad, 为8 + 1 + 3 = 12
}; // 12,结构体整体大小为最大字节数为int(4)的整数倍
// 如果子类中所有成员的自然对齐要求小于父类的话,则子类需要按照父类的自然要求进行对齐,也可以理解成嵌套结构体的字节数
// 嵌套结构体字节数需要将嵌套结构体展开计算偏移量(为接下来要放置变量的整数倍),而不是整个计算。
struct Derived : public Base
{
char d;
};
sizeof 计算数组的字节数
// 某32位系统下,C++程序,请计算sizeof 的值,按程序执行顺序,应该输出什么
// 数组名作函数的参数,主调函数和被调函数共用一段存储单元
void Foo ( char str[100] )
{
printf("sizeof(str)=%d \n", sizeof(str) );
}
void main()
{
char str[] = "www.ibegroup.com";
char *p1 = str ;
int n = 10;
void *p2 = malloc( 100 );
printf("sizeof(str)=%d \n", sizeof(str) ); // 17
printf("sizeof(p1)=%d \n", sizeof(p1) ); // 4
printf("sizeof(n)=%d \n", sizeof(n) ); // 4
printf("sizeof(p2)=%d \n", sizeof(p2) ); // 4
Foo(str); // 4
}
sizeof 计算类的字节数
1.假设sizeof(long)等于4
2. 给定一个类C,该类不继承任何其他类
3. 该类的内存布局采用编译器默认逻辑
4. 该类中包含一个int类型非静态成员和两个char类型的非静态成员变量
则,sizeof(C)表达式可能的求值结果是多少? ABD
A、16 // char int char 和考虑定义虚函数;如果该类定义了虚函数,则需要增加一个虚函数表指针成员变量,其大小为sizeof(long)
B、8 // char char int ,不考虑虚函数
C、10
D、12 // char int char,不考虑虚函数
#include默认路径搜索
#include 和 #include “filename.h” 有什么区别? D
A、对于#include ,编译器从用户的工作路径开始搜索filename.h;对于#include “filename.h” ,编译器从用户的工作路径开始搜索filename.h
B、对于#include ,编译器从用户的工作路径开始搜索filename.h;对于#include “filename.h” ,编译器从标准库路径开始搜索filename.h
C、对于#include ,编译器从标准库路径开始搜索filename.h;对于#include “filename.h” ,编译器从标准库路径开始搜索filename.h
D、对于#include ,编译器从标准库路径开始搜索filename.h;对于#include “filename.h” ,编译器从用户的工作路径开始搜索filename.h
容器vector运算符重载
vector::iterator重载了下面哪些运算符? ABD
A、++
B、==
C、>>
D、*(前置)
运算符类型转换
C++运算法匿名转换向高类型转换。
已知:char a ; float b ; double c ; 则执行语句:c = a + b + c; 后变量c的类型为:double
以下两条输出语句,按照顺序,输出什么
float a = 1.0f;
cout << (int)a << endl;
cout << (int&)a << endl;
cout << boolalpha << ( (int)a == (int&)a ) << endl; // false
float b = 0.0f;
cout << (int)b << endl;
cout << (int&)b << endl;
cout << boolalpha << ( (int)b == (int&)b ) << endl; // true
二、C++面向对象
面向对象语言特性
下列对C++语言特点描述正确的选项是?ABD
A、可以担负起以模版为特征的泛型化编程
B、在C语言的基础上进行扩充和完善,使C++兼容了C语言的面向过程特点,又成为了一种面向对象的程序设计语言
C、可以使用抽象数据类型进行面向过程的编程
D、可以使用多继承、多态进行面向对象的编程
多态性
关于C++的多态性,下列描述正确的是?ABCD
A、存在虚函数的类都有一个一维的虚函数表叫做虚表,类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的
B、在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数
C、多态用虚函数来实现,结合动态绑定
D、多态性是一个接口多种实现,是面向对象的核心,分为类的多态性和函数的多态性
E、用virtual关键字申明的函数叫做虚函数,虚函数不一定是类的成员函数
初始化列表的初始化顺序
初始化列表的初始化顺序:为了避免两次构造,推荐使用类构造函数初始化列表。初始化列表不是按照列表的顺序进行的;是按照内存模型中的成员变量的顺序(也即类声明的定义顺序)进行的;为了避免出现依赖的问题,应当让参数列表和在类内的成员变量声明保持一致。
class A
{
public:
A():n2(0), n1(n2+0){}
void print() { cout << "n1: " << n1 << ", n2: " << n2 << endl; }
private:
int n1;
int n2;
};
int main(int, char**)
{
A a;
// 初始化的顺序是由类内定义的顺序决定的,先初始化 n1,而 n1 由未被赋值的 n2 初始化
a.print(); // n1: -858993458, n2: 0
return 0;
}
派生类构造函数和析构函数的执行顺序
派生类构造函数必须对这三类成员进行初始化,其执行顺序:调用基类构造函数;调用子对象的构造函数;派生类的构造函数体。
析构函数在执行过程中也要对基类和成员对象进行操作,但它的执行过程与构造函数正好相反,即对派生类新增普通成员进行清理;调用成员对象析构函数,对派生类新增的成员对象进行清理;调用基类析构函数,对基类进行清理。
有程序如下:
#include<iostream>
using namespace std;
class A {
public:
A() { cout << "A"; }
};
class B { public:B() { cout << "B"; } };
class C : public A {
B b;
public:
C() { cout << "C"; }
};
int main() {
C obj; // "ABC"
return 0;
}
类构造函数次数
假定AB为一个类,则执行“AB a(2), b[3],*p[4];”
语句时调用该类构造函数的次数为: 4 。
a(2)调用1次带参数的构造函数,b[3]调用3次无参数的构造函数,*p[4]指针没有给它分配空间,没有调用构造函数。所以共调用构造函数的次数为4
虚函数实现原理
在下面的代码中,函数调用语句base->print();会产生什么行为?
#include <stdio.h>
class Base
{
public:
virtual void print()
{
printf("%d\n", counter++);
}
private:
static int counter;
};
// static
int Base::counter = 0;
int main()
{
Base* base = nullptr; // this 指针为空指针
base->print(); // 运行崩溃,虚函数使用虚函数表实现;带有虚函数的类都会有一个由编译器插入的成员变量:虚函数表指针
return 0;
}
给定下面的代码,使用main函数的中的局部变量b分别调用Func1、Func2、Func3和Func4,哪个函数调用会产生崩溃?
class Base {
public:
Base() : b(1) {}
void Func1() { printf("Func1 b = %d\n", b); } // 崩溃,this指针为空指针,使用空指针访问成员变量会引发崩溃
virtual void Func2() { printf("Func2\n"); } // 崩溃,函数是虚函数,需要首先使用空指针获取虚函数表指针成员变量,引发崩溃
void Func3() { printf("Func3\n"); } // 不崩溃,this指针为空指针,但是,没有访问成员变量,不崩溃
static void Func4() { printf("Func4\n"); } // 不崩溃,函数是静态成员函数,函数内部不能使用this,不崩溃
private:
int b;
};
int main() {
Base* b = nullptr; // this 指针为空指针
printf("done!\n");
return 0;
}
重载、重写和隐藏
#include <stdio.h>
在下面的代码中,打印结果是什么?
class Base
{
public:
Base(int c1)
{
counter = c1;
}
virtual void printCounter()
{
printf("%d\n", counter++);
}
protected:
int counter = 1;
};
class Derived : public Base
{
public:
Derived(int c1)
: Base(c1)
{
}
void printCounter() override // 子类继承父类并重写了父类的虚函数
{
printf("%d\n", counter);
}
};
int main()
{
Derived d(2); // 使用子类实例可以初始化父类对象,并且默认采用拷贝构造
Derived* pd = &d;
Base& rb = *pd; // 父类引用和指针都可以指向子类实例
rb.printCounter(); // 2
Base b = d;
b.printCounter(); // 2
return 0;
}
三、C++内存管理
指针数组和运算符优先级别: [] > * > ++
指针数组:数组元素都是相同类型的指针,相同类型的指针是说指针所指向的对象类型是相同的。
运算符优先级别: [] > * > ++
int *p[5];
定义了一个指针数组,在指针数组的定义中有两个运算符:*和[ ]
,运算符[ ]的优先级高于*,所以*p[5]
等价于*(p[5])
,p[5]表示一个数组,而*
表示后面的对象为指针变量,合在一起*p[5]
表示一个指针数组。该数组包含5个元素,每个元素都是指向int型的指针。
二维指针使用
执行以下程序段后,m的值为 ( )。
int a[2][3]={{1,2,3},{4,5,6}};
int m,*p=&a[0][0];
m=(*p)*(*(p+2))*(*(p+4)); // 1 * 3 * 5 = 15
堆与栈
C++中什么数据分配在栈中? ACDE
A、函数调用参数
B、静态局部变量
C、局部变量
D、函数返回值
E、函数返回地址
Heap与stack的差别描述正确的是? BCD
A、Heap空间有限,Stack是很大的自由存储区
B、C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。
C、程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行
D、Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。
四、C++11 及现代 C++特性
智能指针和移动语义
智能指针类std::unique_ptr支持移动构造和移动赋值,在变量定义时使用等号进行初始化会调用构造函数而不是赋值函数。
在下面的代码中,初始化abc2变量时调用的是什么函数?
std::unique_ptr<std::string> abc(new std::string("abc"));
std::unique_ptr<std::string> abc2 = std::move(abc); // 移动构造
nullptr 和 NULL的区别
在 C++ 中,NULL 的定义实际上是一个整数值 0,而不是一个真正的指针类型。
在函数重载和模板编程中这可能会导致一些问题和歧义。
为了解决这个问题,C++11 引入了一个新的关键字 nullptr,用于表示空指针。
nullptr 是一种特殊类型的字面值,类型为 std::nullptr_t,定义为: typedef decltype(nullptr) nullptr_t
,可以隐式转换为任何指针类型。
与 NULL 不同,nullptr 是一个真正的指针类型,因此可以避免一些由于 NULL 是整数类型而引起的问题。
五、进程通信
进程间通信
关于几种进程间通信方式描述,下列选项正确的是? ABCD
A、共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信
B、管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系
C、消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点
D、信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段
协程和线程
关于协程描述,下列正确的是?ABCE
A、支持跨平台、跨体系架构、无需线程上下文切换的开销、无需原子操作锁定及同步的开销、方便切换控制流,简化编程模型
B、进行阻塞(Blocking)操作,如IO时,会阻塞掉整个程序,可以使用异步IO操作来解决
C、协程是协作式,线程是抢占式
D、协程没有自身的寄存器上下文和栈,使用系统的
E、协程是一种用户态的轻量级线程。
程序什么时候应该使用线程,什么时候单线程效率高,描述正确的是? CD
A、多CPU系统中,使用线程会降低CPU利用率
B、耗时的操作不应使用线程,会降低应用程序响应时间
C、并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求
D、一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改,其他情况都使用单线程
同一进程下的线程可以共享以下? CD
A、thread ID
B、register set
C、stack
D、data section
六、算法
链表插入
在一个单链表结构中,指针p指向链表的倒数第二个结点,指针s指向新结点,则能将s所指的结点插入到链表末尾的语句组是? BCD
A、p=p->next; s->next=p; p->next=s
B、p=(*p).next; (*s).next=(*p).next; (*p).next=s
C、p=p->next; s->next=p->next; p->next=s
D、s->next=NULL; p=p->next; p->next=s
七、推荐书籍
学习新东西的时候,注重知识的体系性和框架的建立,然后集中时间,快速抓住领域的主线,突出重点去学习。对于细枝末节的零散内容,可以留到实践的时候,用到了再去查看。
书名 | 推荐情况 | 预估时间 |
---|---|---|
《C++ Primer》 | 推荐全看,按照目录索引查漏补缺 | 2个月 |
《Effective C++》 | 推荐全看,掌握 C++ 编码规范 | 3周 |
《深度探索 C++ 对象模型》 | 推荐前3章,掌握内存模型整体概念 | 2周 |
《STL 源码剖析》 | 推荐掌握vector, map, quene STL 源码和底层机制 | 3周 |
八、推荐网站
C++ 日常开发一定要记得以下几个网站,可以随时查阅一些语法的用法和标准库:
- https://en.cppreference.com/w/
- https://cplusplus.com
- https://isocpp.org
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!