CPP面试题(3)

1. C 语言和 C++ 语言的区别

  1. C 语言是面向过程的语言,而 C++ 支持面向对象,所以 C 语言自然没有面向对象的封装、继承、多态等特性,也不支持面向对象的一些语法;
  2. C++ 支持函数重载,C 语言不支持;
  3. C 程序中如果函数没有任何参数需要将参数定义为 void 以此来限定函数不可传递任何参数,如果不进行限定让参数表默认为空其意义是可以传递任何参数,在 C++ 中,不带参数的函数表示函数不能传递任何参数;
  4. C 语言 struct 中不能有函数,而 C++ 语言 struct 中可以有函数;
  5. C 语言函数参数不支持默认值,而 C++ 语言支持参数默认值;
  6. C++ 语言支持内联函数,而 C 语言不支持;
  7. C++ 语言支持引用,而 C 语言不支持;
  8. C 语言采用 malloc 和 free 函数动态申请和释放内存,而 C++ 使用 new 和 delete 运算符;
  9. C 语言中只有局部和全局两个作用域,而 C++ 中有局部、全局、类、名称空间作用域;

2. C++ 和 C 中 struct 的区别以及和 class 的区别

  1. C++ 和 C 中 struct 的区别:
    1. C 的结构体不允许有函数存在,C++ 的结构体允许有内部成员函数,并且允许该函数是虚函数;
    2. C 的结构体内部成员不能加权限,默认是 public,而 C++ 的结构体内部成员权限可以是 public、protected、private,默认 public;
    3. C 的结构体是不可以继承,C++ 的结构体可以从其它的结构体或者类继承;
    4. C 中的结构体不能直接初始化数据成员,C++ 中可以;
    5. C 中使用结构体需要加上 struct 关键字,或者对结构体使用 typedef 取别名后直接使用,而 C++ 中使用结构体可以省略 struct 关键字直接使用 struct student{ int age; string name; } typedef struct student student2; //C中取别 struct student stu1; // C 中正常使用 student2 stu2; // C 中通过取别名的使用 student stu3; // C++ 中使用,C 中直接使用编译不通过
  2. struct 和 class 的区别:
    1. struct 一般用于描述一个数据结构集合,而 class 是对一个对象数据的封装;
    2. struct 中默认访问控制权限是 public,而 class 中默认的访问控制权限是 private struct A { int iNum; // 默认访问控制权限是 public } class B { int iNum; // 默认访问控制权限是 private } ;
    3. 在继承关系中,struct 默认是公有继承,而 class 是私有继承;
    4. class 关键字可以用于定义模板参数,而 struct 不能 template <class t=""> int func(const T& t, const Y& y) { };

3. C 语言里面 volatile,可以和 const 同时使用吗

  1. volatile 限定符是用来告诉计算机,所修饰的变量的值随时都会进行修改的。用于防止编译器对该代码进行优化。通俗的讲就是编译器在用到这个变量时必须每次都小心地从内存中重新读取这个变量的值,而不是使用保存在寄存器里的备份;
  2. const 和 volatile 可以一起使用,volatile 的含义是防止编译器对该代码进行优化,这个值可能变掉的。而 const 的含义是在代码中不能对该变量进行修改;
  3. 因此,它们本来就不是矛盾的;

4. extern 的作用,extern变量在哪个数据段,为什么要 extern C

  1. extern 可以置于变量声明或者函数声明前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其它文件中寻找其定义;
  2. extern 变量表示声明一个变量,表示该变量是一个外部变量,也就是全局变量,所以 extern 修饰的变量保存在静态存储区(全局区),全局变量如果没有显示初始化,会默认初始化为 0,或者显示初始化为 0 ,则保存在程序的 BSS 段,如果初始化不为 0 则保存在程序的 DATA 段;
  3. extern “C” 的作用是为了能够正确的实现 C++ 代码调用 C 语言代码。加上 extern “C” 后,会指示编译器这部分代码按照 C 语言(而不是 C++)的方式进行编译。由于 C++ 支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译 C 语言代码的函数时不会带上函数的参数类型,一般只包括函数名。 这个功能十分有用处,因为在 C++ 出现以前,很多代码都是 C 语言写的,而且很底层的库也是 C 语言写的,为了更好的支持原来的 C 代码和已经写好的 C 语言库,需要在 C++ 中尽可能的支持 C,而 extern “C” 就是其中的一个策略。

5. const 的用法

  1. 用在变量身上,表示该变量只读,不能对它的值进行修改 const int a = 10; a = 20; // 编译会报错,因为 a 只读,不能对它进行修改;
  2. 结合指针一起使用 const int * p;// 常量指针 int * const p;// 指针常量 const int * const p; const int * p是常量指针,表示指针变量 p 所指向的内容不能修改,指针变量 p 的内容可以修改; int * const p 是指针常量,表示指针变量 p 的内容不能修改,指针变量 p 所指向的内容可以修改; const int * const p 表示指针变量 p 的内容和所指向的内容都不可以修改;
  3. const 用于函数参数 void foo(const int * p); void foo(const int & p);const 用于形参时说明形参在函数内部不能被改变,这是非常有用的,有时候函数参数传递指针或者引用,在函数内部不希望对指针和引用指向的数据进行修改,可以加上 const;
  4. 在类中修饰成员方法,防止在方法中修改非 static 成员 class A { public: int a; void fun() const { a = 20; }// 错误,const 修饰的成员方法中不能修改非静态成员变量 } };
  5. const 修饰类的成员变量 class T { public: T() : a(10) { } private: const int a; static const int b; }; const int T::b = 20;类的成员变量可以分为静态的和非静态的,如果 const 修饰的是静态的成员变量,可以在构造函数中对该变量进行初始化;如果 const 修饰的是静态的成员变量,则需要在类外对该变量进行初始化。

6. const 和 define 的区别

const 在 C 语言中表示只读,编译器禁止对它修饰的变量进行修改,在 C++ 中增加了常量的语义。而 define 用于定义宏,而宏也可以用于定义常量。它们的区别有:

  1. const 生效于编译阶段,而 define 生效于预处理阶段;
  2. define只是简单的字符串替换,没有类型检查,而 const 有对应的数据类型,编译器要进行判断的,可以避免一些低级的错误;
  3. 用 define 定义的常量是不可以用指针变量去指向的,用 const 定义的常量是可以用指针去指向该常量的地址的;
  4. define 不分配内存,给出的是立即数,有多少次使用就进行多少次替换,在内存中会有多个拷贝,消耗内存大,const 在静态存储区中分配空间,在程序运行过程中内存中只有一个拷贝;
  5. 可以对 const 常量进行调试,但是不能对宏常量进行调试。

7. static 关键字的作用

static 是一个关键字,可以用来修饰局部变量、全局变量、成员变量、函数和成员方法。

主要作用有:限制数据的作用域、延长数据的生命周期、修饰成员可以被该类所有对象共享。

  1. 限制数据的作用域(隐藏) 所有没有加 static 的全局变量和函数都具有全局可见性,其它源文件中也可以访问。被 static 修饰的全局变量和函数只能在当前源文件中访问,其它源文件访问不了,利用这个特性可以在不同的文件中定义同名变量和同名函数,而不必担心命名冲突;
  2. 延长数据的生命周期 普通的局部变量出了作用域就会释放,而静态变量存储在静态区,知道程序运行结束才会释放;
  3. 静态成员被该类所有对象共享 static 关键字可以修饰类中的成员变量和成员方法,被称为静态成员变量和静态成员方法,静态成员拥有一块单独的存储区,不管创建多少个该类的对象,所有对象都共享这一块内存。静态成员本质上属于类,可以通过类名直接访问;

加分回答

  1. 静态变量默认初始化值为0,如果没有显示初始化静态变量或者初始化为0的静态变量会存储在BSS段,而初显示初始化的静态变量存储在DATA段;
  2. 静态成员函数中不能访问普通的成员变量,只能访问静态成员变量,并且在静态成员函数中没有 this 指针;

8. C++ 中的四种类型转换

使用 C 风格的类型转换可以把想要的任何东西转换成我们需要的类型,但是这种类型转换太过松散,对于这种松散的情况,C++ 提供了更严格的类型转换,可以提供更好的控制转换过程,并添加 4 个类型转换运算符,使转换过程更规范:static_castdynamic_castconst_castreinterpret_cast

  1. static_cast静态转换 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换 - 进行上行转换(把派生类的指针或引用转换成基类表示)是安全的 - 进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的 用于基本数据类型之间的转换,如把 int 转换成 char,把 char 转换成 int。这种转换的安全性也要开发人员来保证;
  2. dynamic_cast动态转换 dynamic_cast主要用于类层次间的上行转换和下行转换 在类层次间进行上行转换时,dynamic_caststatic_cast 的效果是一样的 在进行下行转换时,dynamic_cast具有类型检查的功能,比 static_cast更安全;
  3. const_cast常量转换 该运算符用来修改类型的const属性 常量指针被转化成非常量指针,并且仍然指向原来的对象 常量引用被转换成非常量引用,并且仍然指向原来的对象 注意:不能直接对非指针和非引用的变量使用 const_cast操作符;
  4. reinterpret_cast重新解释转换 这是最不安全的一种转换机制,最有可能出问题 主要用于将一种数据类型从一种类型转换为另一种类型,它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针;

9. auto 和 decltype 如何使用

C++11 提供了多种简化声明的功能,尤其在使用模板时。

  1. auto 实现自动类型推断,要求进行显示初始化,让编译器能够将变量的类型设置为初始值的类型: auto a = 12; auto pt = &a; double fm(double a, int b) { return a + b; } auto pf = fm;简化模板声明 for(std::initializer_list
  2. decltype 将变量的类型声明为表达式指定的类型: decltype(expression) var; decltype(x) y;// 让y的类型与x相同,x是一个表达式;

10. C++11 中的可变参数模板新特性

在 C++11 之前,类模板和函数模板只能含有固定数量的模板参数。

C++11 增强了模板功能,它对参数进行了高度泛化,允许模板定义中包含 0 到任意个、任意类型的模板参数,这就是可变参数模板。可变参数模板的加入使得 C++11 的功能变得更加强大,能够很有效的提升灵活性。

  1. 可变参数函数模板语法: template<typename... t=""> void fun(T...args){ // 函数体 } 模板参数中, typename(或者 class)后跟 … 就表明 T 是一个可变模板参数,它可以接收多种数据类型,又称模板参数包。fun() 函数中,args 参数的类型用 T… 表示,表示 args 参数可以接收任意个参数,又称函数参数包;
  2. 可变参数类模板语法: template <typename... types=""> class test
  3. 展开参数包的方式 - 可变参数函数模板可以采用递归方式、逗号表达式 + 初始化列表的方式展开参数包; - 可变参数类模板可以采用递归+继承的方式展开参数包。 加分回答 C++ 11 标准提供的 tuple 元组类就是一个典型的可变参数模板类,它的定义如下: template <typename... types=""> class tuple;</typename...></typename...></typename...>