BIGO 面经C++总结

1、C++和C语言最大的区别是什么

大体上:C是面向过程的语言,而C++是面向对象的语言。

面向过程:类调用时需要实例化,开销比较大,单片机、嵌入式开发。面向对象封装、继承、多态,易维护、易复用、易扩展

C++更是拓展了面向对象设计的内容,如类Class、继承、虚函数等、以及一个非常强大的C++标准模板库STL,另外一个Boost库现在也归属C++标准库。

具体:

1)动态内存管理:C语言通过malloc和free来进行堆内存的分配和释放,而C++是通过new和delete来管理堆内存的

malloc只负责开辟内存,没有初始化功能;开辟内存需要传入字节数,如malloc(100);表示在堆上开辟了100个字节的内存.

new不但开辟内存,还可以进行初始化,如new int(10);

int *p1 = (int *)malloc(sizeof(int));// 指定大小,且类型转换
int *p2 = new int(10);// 初始化了

2)struct和class:class是对struct的扩展,struct默认的访问权限是public,而class默认的访问权限是private。C++中,struct关键字不仅可以用来定义结构体,它也可以用来定义类, 在C语言中struct定义的变量中不能有函数,而在C++中可以有函数。

3)  C++函数默认值: int FUN(int a = 10);代表没有传参调用的时候,自动给a赋一个10的初始值。但C没有。

4)  函数重载:由于参数类型不同可以构成重载compare(int a,int b)  compare(double a,double b)

5)inline内联函数:只有C++有,直接写在头文件中。

扫描二维码关注公众号,回复: 9859306 查看本文章

2、面向对象:三大特点:封装、继承、多态

封装:代码模块化。类可以把自己的数据和方法只让可信的类或者对象访问private   public  protected,隐藏细节

继承与虚继承:代码复用。编写新的类,既能共享其基类,又能根据需要覆盖或添加行为。扩展已存在的代码模块

多态(虚函数)一个接口多种实现。在运行时根据对象的动态类型来选择运行函数的哪个版本。只作用于虚函数,需要通过基类指针或引用调用。

3、说一下指针和引用的区别

本质:引用是别名,指针是实体,这个变量存储的是一个地址。

具体:

1、指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化

2、指针在运行时可以改变所指向的值,而引用一旦与某个对象绑定后就不再改变

3、可以有const指针,但是没有const引用;

4、sizeof

对齐:结构体的大小跟结构体成员对其有密切关系,而并非简单地各个成员的大小之和。比如两个结构体AA和BB。,使用sizeof分别得出结果是24和16

结构体成员对齐的规则:(1)结构体大小等于结构体内最大成员大小的整数倍;(2)结构体内的成员的首地址相对于结构体首地址的偏移量是其类型大小的整数倍,比如说double型成员相对于结构体的首地址的偏移量应该是8的整数倍;(3)为了满足规则1和规则2编译器会再结构体成员之后进行字节填充。

1)空结构体:sizeof为1,编译器为空类实例化分配一个字节用于占位。

2)  :总大小为结构体最宽基本类型成员大小的整数倍,考虑偏移和对齐。要注意的是static变量不属于类的一部分,如果类中定义了static变量,求sizeof时可以忽略它们。

3)带虚函数的类:虚函数放在虚表中,需要存放一个指向虚表的指针。它们存放于同一个虚表中,因此只需要一个指针。

4)普通继承:普通继承的空间计算结果应当是sizeof(基类)+sizeof(派生类),然后考虑对齐

5)虚继承:如果子类和基类都有虚函数,各自用各自的虚表,且虚继承多一个指针

                         以64位为例
// 1.空结构体  
struct S1  { };  
sizeof(S1); //值为1,空结构体也占内存

//2. 结构体
struct S2  
{  
    char a;  //1
    int b;  //4
};  
sizeof(S2); //值为8,字节对齐,在char之后会填充3个字节。  

// 3. 带虚函数的类
class S3
{
    int a;
    virtual void fun(){}
    virtual void fun1(){}
    virtual void fun3(){}
};
sizeof(S3);    //值为16   sizeof(int)要补 + sizeof(虚表指针)

// 4. 普通继承
class S2
{
    int num;
    char str;
};
class S4 : public S2
{
    char str2;
    int num2;
};
sizeof(S4);   //值为16  sizeof(类A) + sizeof(类B)

// 5.虚继承
class A
{
    int num;
    virtual void fun(){}// 用自己的虚表
};
class B : virtual public A // 虚继承指针
{
    int num2;
    virtual void fun1(){} // 用自己的虚表
};
sizeof(B); // sizeof(A) + sizeof(B) + sizeof(虚继承指针) +  sizeof(A类虚表指针) +  sizeof(B类虚表虚指针)

6) 添加构造函数与析构函数,不会增加字节数量。

7)数组的sizeof等于占用内存数

      注意:1 ) 字符数组表示字符串时,需要考虑最后的/0

                 2)    数组为形参时,sizeof值为指针。

 // 数组
 char a[10];
 cout<<sizeof(a)<<endl;// 10,
 // 字符数组
 char n[]="abc";
 cout<<sizeof(n)<<endl;// 4, 字符串数组,加入‘/0’
 // 数组作为形参
 void fun(char a[3]){
       int c=sizeof(a);//  4,作为形参时指针类型
  }

综合实例

class A{  64位
  int a; // 4,对齐+4
  long b; // 8
  void func(); // 0
  virtual void func2();  // 4,虚函数表,对齐+4
};

cout<<sizeof(A)<<endl;// 24,
普通继承:sizeof(基类)+sizeof(派生类),考虑对齐

5、说一下内联函数,他和宏定义有什么不一样

inline一般只用于如下情况:(1)一个函数不断被重复调用。(2)函数只有简单的几行,且函数不包含for、while、switch语句

(3)在类中声明同时定义的成员函数,自动转化为内联函数。

作用:inline是指嵌入代码,和普通函数相比可以加快程序运行的速度

与宏定义区别:内联函数要做参数类型检查,而宏定 义则不会;

6、const 在*左和右的区别,const修饰成员函数有什么作用,如果想修改成员变量呢

const char* p=greeting; //底层const,所指内容为常量,常见方式。
char* const p=greeting  //顶层const.指针(地址)是常量,表明不能指向不同的东西。

const成员函数:只能读取数据成员,不能改变数据成员。

7、成员变量和局部变量的区别,成员变量和静态变量的区别,静态成员函数

1)成员变量和局部变量的区别

       成员变量:

          ①成员变量定义在类中,在整个类中都可以被访问。

          ②成员变量随着对象的建立而建立,随着对象的消失而消失,存在于对象所在的堆内存中。

          ③成员变量有默认初始化值。

      局部变量:

          ①局部变量只定义在局部范围内,如:函数内,语句内等,只在所属的区域有效。

          ②局部变量存在于栈内存中,作用的范围结束,变量空间会自动释放。

          ③局部变量没有默认初始化值 

      在使用变量时需要遵循的原则为:就近原则

      首先在局部范围找,有就使用;接着在成员位置找。

2)成员变量和静态变量的区别

static修饰的变量称为静态变量,其实质上就是一个全局变量。如果某个内容是被所有对象所共享,那么该内容就应该用静态修饰;

      1、两个变量的生命周期不同

            成员变量随着对象的创建而存在,随着对象被回收而释放。

            静态变量随着类的加载而存在,随着类的消失而消失。

      2、调用方式不同

            成员变量只能被对象调用。

            静态变量可以被对象调用,还可以被类名调用。

      3、别名不同

            成员变量也称为实例变量。

            静态变量也称为类变量。

      4、数据存储位置不同

            成员变量存储在堆内存的对象中,所以也叫对象的特有数据。

            静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据。

8、堆和栈都存了啥变量

内存分配:栈、堆、自由存储区、静态存储区、常量存储区

9、new和malloc的区别,new之后发生了什么,new之后构造函数什么时候被调用

动态内存管理:C语言通过malloc和free来进行堆内存的分配和释放,而C++是通过new和delete来管理堆内存的

malloc只负责开辟内存,没有初始化功能;开辟内存需要传入字节数,如malloc(100);表示在堆上开辟了100个字节的内存.

new不但开辟内存,还可以进行初始化,如new int(10);

int *p1 = (int *)malloc(sizeof(int));// 指定大小,且类型转换
int *p2 = new int(10);// 初始化了

10、实现一个只能在堆上(栈上)生成对象的类

简述答案:

1、只能在上生成对象:将析构函数设置为私有

原因:编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性。若析构函数不可访问,则不能在栈上创建对象。

类对象只能建立在堆上,即不能直接调用类的构造函数,首先想到是将构造函数设为私有,但是new的第二部分也需要间接调用构造函数,所以这种方法不行。就考虑到将析构函数设为私有。

class  A 
{ 
public : 
    A(){} 
    void  destory(){ delete   this ;} 
private : // 将析构函数设为私有
    ~A(){} 
};

2、只能在上生成对象:将new和delte重载为私有

原因:堆上使用new,两阶段:第一阶段,使用new在堆上寻找可用内存,分配给对象;第二阶段,调用构造函数生成对象。将new操作设置为私有,那么第一阶段就无法完成,就不能够再堆上生成对象。

class  A 
{ 
private : // 将new 重载为私有,相应的delete也要配对
    void * operator  new ( size_t  t){}      // 注意函数的第一个参数和返回值都是固定的  
    void  operator  delete ( void * ptr){}  // 重载了new就需要重载delete  
public : 
    A(){} 
    ~A(){} 
};

11、为什么析构函数可以为虚函数,构造函数不可以

1)析构函数设为虚函数

析构函数的作用在对象撤销前把类的对象从内存中撤掉,通常系统只会执行基类的析构函数,不执行派生类的析构函数。

2)构造函数不设置为虚函数

构造一个对象时,需要知道对象实际类型,但是虚函数是在运行期间确定实际类型的。虚函数的执行依赖于虚函数表,而虚函数表是在构造函数中进行初始化的,即初始化虚表指针使得指向虚函数表。而在构造对象期间,虚函数表还没有被初始化

12、c++11新特性有哪些


13、你知道的stl容器有哪些,哪些是c++11新增的
14、vector的capacity和size的区别,reverse函数,clear时会释放占用的内存吗,怎么释放呢
15、map的底层是用什么实现的,查询时间复杂度,插入和删除呢

 

发布了176 篇原创文章 · 获赞 84 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/try_again_later/article/details/100520201