面试之CPP基础知识-6


#int main(int argc, char *argv[])里的参数,你了解吗?

参数的含义是程序在命令行下运行的时候,输入了argc 个参数,每个参数以char*类型存入 argv[],第一个参数为程序的名称。

#volatile关键字的作用?

volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,系统总是重新从它所在的内存读取数据。可以用在多线程中,每次读取到修改后的值。

#如果有一个空类,它会默认添加哪些函数?

1
2
3
4
5
6
Empty();  // 缺省构造函数
Empty( const Empty& );  // 拷贝构造函数
~Empty();  // 析构函数
Empty& operator=( const Empty& ); // 赋值运算符
Empty* operator&(); // 取址运算符
const Empty* operator&() const; // 取址运算符 const

只有你需要用到这些函数并且你又没有显示的声明这些函数的时候,编译器才会贴心的自动声明相应的函数。

https://developer.aliyun.com/article/3763

#C++中标准库是什么?

C++ 标准库可以分为两部分:

标准函数库: 这个库是由通用的、独立的、不属于任何类的函数组成的。函数库继承自 C 语言。

  • 输入/输出 I/O、字符串和字符处理、数学、时间、日期和本地化、动态分配、其他、宽字符函数

面向对象类库: 这个库是类及其相关函数的集合。

  • 标准的 C++ I/O 类、String 类、数值类、STL 容器类、STL 算法、STL 函数对象、STL 迭代器、STL 分配器、本地化库、异常处理类、杂项支持库

https://www.runoob.com/cplusplus/cpp-standard-library.html

#const char* 与string之间的关系?

  1. string 是c++标准库提供的字符串类,封装了对字符串的操作

  2. const char *是指向一个常量字符串的指针

  3. 可以用const char*给string类初始化,用string.c_str()给const char *初始化。

  4. 三者的转化关系如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// a)  string转const char* 
string s = "abc"; 
const char* c_s = s.c_str(); 
// b)  const char* 转string,直接赋值即可 
const char* c_s = "abc"; 
string s(c_s); 
// c)  string 转char* 
string s = "abc"; 
char* c; 
const int len = s.length(); 
c = new char[len+1]; 
strcpy(c,s.c_str()); 
// d)  char* 转string 
char c[] = "abc"; 
string s(c); 
// e)  const char* 转char* 
const char* cpc = "abc"; 
char* pc = new char[strlen(cpc)+1]; 
strcpy(pc,cpc);
// f)  char* 转const char*,直接赋值即可 
char c[] = "abc"; 
const char* cpc = c;

#你什么情况用指针当参数,什么时候用引用,为什么?

用指针的情况一是你考虑到存在不指向任何对象的可能(在这种情况下,你能够设置指针为空),二是你需要能够在不同的时刻指向不同的对象(在这种情况下,你能改变指针的指向),其他情况一般都使用引用,它可以避免判断是否为空提高效率,并且不会像普通的值传递还需要拷贝实参。

https://blog.csdn.net/wyg1065395142

https://blog.csdn.net/lyd_253261362

#静态绑定和动态绑定?

  • 静态类型:对象在声明时采用的类型,在编译期既已确定;
  • 动态类型:通常是指一个指针或引用,目前所指对象的类型,是在运行期决定的;
  • 静态绑定:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期;
  • 动态绑定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;
    • 在继承体系中只有虚函数使用的是动态绑定,其他的全部是静态绑定;

#如何设计一个计算仅单个子类的对象个数?

1、为类设计一个static静态变量count作为计数器;

2、类外初始化count;

3、在构造函数~~、拷贝构造函数、赋值构造函数~~中对count进行+1;

4、在析构函数中对count进行-1;

#怎么快速定位错误出现的地方?

  • 查看错误信息和日志:首先,仔细阅读错误信息和日志,它们通常会提供关于错误类型、发生位置和相关调用栈的信息。错误信息可能包含文件名、行号、函数名等,这些信息可以帮助你快速定位到错误的源代码位置。

  • 使用调试器:调试器是一种强大的工具,可以逐行跟踪代码的执行过程,查看变量的值、调用堆栈等信息。通过在出现错误的地方设置断点,你可以在运行时停止程序并检查当前的状态,帮助你找到问题所在。

  • 日志输出和断言:在代码中适当地添加日志输出和断言语句,可以帮助你追踪程序的执行过程,并在特定条件下中断程序执行。通过在关键位置输出日志信息,你可以了解程序的执行流程和变量的值,从而更容易定位错误。

  • 缩小搜索范围:如果你已经确定错误发生在某个函数或代码块内部,可以采用二分法或逐步注释法来缩小搜索范围。通过逐渐注释掉代码段或只保留关键部分,可以确定错误发生的具体位置。

  • 使用工具和技术:利用各种可用的工具和技术来辅助定位错误,如代码静态分析工具、内存泄漏检测工具、性能分析器等。这些工具可以提供更详细的分析和报告,帮助你找出潜在的问题所在。

  • 与他人讨论和协作:如果你遇到困难或无法确定错误的来源,寻求他人的帮助是一个明智的选择。与其他开发者或论坛社区交流,描述你的问题和尝试过的解决方法,他们可能会提供新的思路和建议。

mark

#成员初始化列表会在什么时候用到?它的调用过程是什么?

  1. 当初始化一个引用成员变量时;

  2. 初始化一个const成员变量时;

  3. 初始化没有默认构造函数的成员对象或者基类对象;

  4. 考虑性能的时候,列表初始化减少一次拷贝

  5. 调用过程?不知道,初始化参数过程:按照类中定义变量的顺序进行初始化 代码

#进行函数参数以及返回值传递时,使用引用的好处有哪些?

对比值传递,引用传参的好处:

  • 在函数内部可以直接对实参修改
  • 提高函数调用和运行的效率(因为没有了传值和生成副本的时间和空间消耗)

用引用作为返回值最大的好处就是在内存中不产生被返回值的副本。

但是不能返回局部变量的引用。因为函数返回以后局部变量就会被销毁。

#说一说strcpy、sprintf与memcpy这三个函数的不同之处

  1. 操作对象不同

① strcpy的两个操作对象均为字符串

② sprintf的操作源对象可以是多种数据类型,目的操作对象是字符串

③ memcpy的两个对象就是两个任意可操作的内存地址,并不限于何种数据类型。

  1. 执行效率不同

memcpy最高,strcpy次之,sprintf的效率最低。

  1. 实现功能不同

① strcpy主要实现字符串变量间的拷贝

② sprintf主要实现其他数据类型格式到字符串的转化

③ memcpy主要是内存块间的拷贝。

1
2
3
4
5
6
char str [50];
int n, a=5, b=3;
n=sprintf(str, "%d plus %d is %d", a, b, a+b);
char cpy[80];
strcpy(cpy, str);
memcpy(cpy, str, 80);

#将引用作为函数参数有哪些好处?

  • 在函数内部可以对此参数进行修改
  • 提高函数调用和运行的效率(因为没有了传值和生成副本的时间和空间消耗)
  • 用指针的情况
    • 一是你考虑到存在不指向任何对象的可能(在这种情况下,你能够设置指针为空),
    • 二是你需要能够在不同的时刻指向不同的对象(在这种情况下,你能改变指针的指向),

#你知道数组和指针的区别吗?

  • 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数。
  • 在向函数传递参数的时候,如果实参是一个数组,数组会退化成指针,指向数组的首地址。
  • 对数组取地址操作后,必须用pointer to array来接受。

#如何阻止一个类被实例化?有哪些方法?

  1. 将类定义为抽象基类(抽象类含有纯虚函数)
  2. 构造函数声明为private

#如何禁止程序自动生成拷贝构造函数?

  • 手动去重写这两个函数,并将他们设置成delete
  • 声明这两个函数,并加上private访问权限,一旦使用在编译阶段就报错。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Uncopyable
{
protected:
    Uncopyable(){}
    ~Uncopyable(){}
private:
	Uncopyable(const Uncopyable&);
    Uncopyable& operator= (const Uncopyable&);
public:
    Uncopyable(const Uncopyable&) = delete;
    Uncopyable& operator= (const Uncopyable&) = delete;
};

#拓展:A a() 和 A a的区别

不要想当然的认为a()会调用默认构造函数,实际上它只是声明,而a才是会调用那个默认构造函数,即定义。

https://blog.csdn.net/m0_37166947/article/details/72856841


在C++中,A a()A a有不同的含义和行为。

  1. A a()

    • 这是一个函数声明的语法,而不是对象的定义或创建。
    • 它声明了一个名为a的函数,该函数没有参数并返回类型为A的值。
    • 这种语法在某些情况下可能会引起误解,因为它看起来像是在创建一个名为a的对象,但实际上它是一个函数声明。
    • 要创建一个对象,应该使用A a;A a{};的形式。
  2. A a

    • 这是一个对象的定义和创建。
    • 它声明了一个名为a的对象,类型为A
    • 如果A是一个类类型,则会调用默认构造函数来初始化对象a
    • 对象a的生命周期将根据其定义的作用域和存储位置进行管理。

from GPT3.5

#Debug和Release的区别?

  1. 调试版本,包含调试信息,所以容量比Release大很多,并且不进行任何优化(优化会使调试复杂化,因为源代码和生成的指令间关系会更复杂),便于程序员调试。Debug模式下生成两个文件,除了.exe或.dll文件外,还有一个.pdb文件,该文件记录了代码中断点等调试信息;

  2. 发布版本,不对源代码进行调试,编译时对应用程序的速度进行优化,使得程序在代码大小和运行速度上都是最优的。(调试信息可在单独的PDB文件中生成)。Release模式下生成一个文件.exe或.dll文件。

  3. 实际上,Debug 和 Release 并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。

#main函数的返回值?

程序运行过程入口是main函数,main()函数返回值类型必须是int,这样返回值才能传递给程序激活者(如操作系统)表示程序正常退出。

main(int args, char **argv) 参数的传递。参数的处理,一般会调用getopt()函数处理,但实践中,这仅仅是一部分,不会经常用到的技能点。

#写一个比较大小的模板函数

1
2
3
4
5
template <typename T>
T Max(T a, T b)
{
    return a > b ? a : b;
}

#strcpy函数和strncpy函数的区别?哪个函数更安全?

  1. 函数原型
1
2
char* strcpy(char* strDest, const char* strSrc)
char *strncpy(char *dest, const char *src, size_t n)
  • strcpy函数: 如果参数 dest 所指的内存空间不够大,可能会造成缓冲溢出(buffer Overflow)的错误情况。
  • strncpy函数:用来复制源字符串的前n个字符,但是它不会在目标字符串追加结束符,容易出现意想不到的输出结果。如果n比较大,会一直填充结束符直到n,效率不太好。 https://blog.csdn.net/qq_26093511/article/details/73338036

因此推荐使用strdup,在堆上开辟,或者strdupa,在栈上开辟,或者用string