面试题4

159 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1.C++中inline的作用

在C++中,为了解决一些频繁调用的小函数大量消耗栈内存的问题,特别地引入了inline修饰符,表示为内联函数。

在系统下,栈空间是有限的,如果频繁大量使用就会造成因栈空间不足而导致程序出错的问题。

在程序编译时,编译器将程序中出现的内联函数调用表达式用内联函数的函数体进行替代,类似于宏展开

int min ( int v1, int v2)
{
	return (v1< v2 ? v1: v2);
}

//代码编写时,是直接按照函数名进行调用
int minval2 = min(i,j);

//实际编译之后,把函数体内部代码进行替换
int minval2 = i < j ? v1: v2;

2.struct和union的区别

1.struct所占用的内存空间是其里面所有变量占用之和;union占用的空间等于其里面单个占用最大变量空间;

2.union有新的数据时,旧的数据会被覆盖,只能存一个数据,但可以有多个类型;struct中的变量不会被覆盖,里面可以存多个数据

3.union用途是将几个不同类型的变量共占一段内存(相互覆盖);struct是一种构造数据类型,其用途是将不同类型的数据组合成一个整体,相当于“类”。

3.static关键字的作用和用法

1.作用

(1)修饰全局变量时,表明一个全局变量只对定义在同意文件中的函数可见;

(2)修饰局部变量时,表明该变量的值不会因为函数终止而丢失;

(3)修饰函数时,表明该函数旨在同意文件中调用;

(4)修饰类的数据成员,表明对该类所有对象这个数据成员都只有一个实例。即该实例归所有对象共有;

(5)用static修饰不访问非静态数据成员的类成员函数。这意味着一个静态成员函数只能访问它的参数、类的静态数据成员和全局变量。

2.用法

static 关键字最基本的用法是:

1、被 static 修饰的变量属于类变量,可以通过类名.变量名直接引用,而不需要 new 出一个类来;

2、被 static 修饰的方法属于类方法,可以通过类名.方法名直接引用,而不需要 new 出一个类来 被 static 修饰的变量、被 static 修饰的方法统一属于类的静态资源,是类实例之间共享的,换言之,一处变、处处变。

在 C++ 中,静态成员是属于整个类的而不是某个对象,静态成员变量只存储一份供所有对象共用。所以在所有对象中都可以共享它。使用静态成员变量实现多个对象之间的数据共享不会破坏隐藏的原则,保证了安全性还可以节省内存。

静态成员的定义或声明要加个关键 static。静态成员可以通过双冒号来使用:即 <类名>::<静态成员名>。

4.C++如何避免拷贝

(1)在新标准下,将拷贝构造函数和拷贝赋值运算符定义为删除函数(delete function),使用方法如下:

struct noCopy {
noCopy();
noCopy(const noCopy&) = delete;
noCopy& operator = (const noCopy&) = delete;
}

(2)将拷贝构造函数和拷贝赋值运算符声明为私有的且不予实现,使用方法如下:

class noCopy {
noCopy();
private:
noCopy(const nocopy&);
noCopy& operator = (const noCopy&);
}

当NoCopy的一个对象去给另一个对象赋值或者执行拷贝操作时,会出现一个未定义的成员导致一个链接时错误,因为函数被定义为private私有的;

当成员函数或者友元函数驶入来呗时,也将出现导致链接时错误,因为没有实现;

综上所述,一般推荐使用方法一来完成阻止拷贝操作。

5.C++中指针和引用的区别

(1)指针是一个变量,存储的是一个地址,指向内存的是一个存储单元;而引用和原来的变量实质上是一个东西,只不过是原变量的一个别名罢了。比如:

int a = 1; 
int *p = &a;//指针
int &b = a;//引用

(2)可以有const指针,但没有const引用;

(3)指针可以有多级指针,但是引用只有一级引用;

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

(5)指针的值在初始化后可以改变,即指向其他的存储单元,而引用在进行初始化后就不会改变了;

(6)siziof引用得到的是所指向变量(对象)的大小,而sizeof指针得到的是指针本身的大小;

(7)指针和引用的自增(++)运算意义不同。指针是对内存地址的自增,而引用是对值的自增。

6.如何让.h文件不被重复引用

(1)使用宏定义避免重复引用

#ifndef _NAME_H
#define _NAME_H
//头文件内容
#endif

(2)使用#pragma once避免重复引用;将其附加到指定文件的最开头位置,则该文件就只会被 #include 一次。

#pragama once
//引入头文件

(3)使用_Pragma操作符 C99 标准中新增加了一个和 #pragma 指令类似的 _Pragma 操作符,其可以看做是 #pragma 的增强版,不仅可以实现 #pragma 所有的功能,更重要的是,_Pragma 还能和宏搭配使用。 当处理头文件重复引入问题时,可以将如下语句添加到相应文件的开头:

_Pragma("once")

7.怎样避免内存泄漏

(1)尽量使用智能指针,而不是手动去管理内存(手动容易出问题)

(2)使用std::string来替代char*。使用std::string无需关心内存管理,它已经很好地在内部实现了内存管理;

(3)避免使用裸指针,除非不用不行(比如要让裸指针指向一个旧的库);

(4)尽量少使用new和delete函数,最好不使用。当我们需要动态内存的时候,构造一个RALL类在析构的时候自动地delete所有动态申请的内存。RALL类在构造函数中申请内存,而在析构函数释放内存,这个一保证RALL对象离开作用域的时候,能自动释放内存;

(5)在你每次需要申请动态内存的时候,先写new 和 delete语句,然后再在中间添加你的功能模块,这样就不会忘记释放内存了。

// CPP program to 
// illustrate how to avoid 
// memory leak
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
  
// function to see memory handling
void func_to_handle_mem_leak()
{
    int* ptr = new int(5);
  
    // body
  
    // Now delete pointer ptr using delete
    delete (ptr);
}
  
// Driver code
int main()
{
  
    // Call function to handle
    // the memory leak
    func_to_handle_mem_leak()
  
        return 0;
}

8.new的作用

在实际的编程中,要处理的数据量在编程时无法确定,那就需要用到动态内存分配(程序运行时进行内存分配)。

而在C++中,通过new运算符来实现动态内存分配。new开辟的空间在堆上。当在局部函数中new出一段新的空间时,该段空间在局部函数调用结束后仍能使用,可以用来向主函数传递参数。

new的使用格式,new出来的是一段空间的首地址,一般用指针来存放这段地址

9.delete和delete[]的区别

(1)delete p1 在回收空间的时候,只有p1[0]这个对象调用了析构函数,其他的对象不会调用自身的析构函数;如果用了delete[],则在回收空间之前,所有对象都会调用自己的析构函数

#include <iostream>;
using namespace std;
 
class T {
public:
  T() { cout << "constructor" << endl; }
  ~T() { cout << "destructor" << endl; }
};
 
int main()
{
  const int NUM = 3;
 
  T* p1 = new T[NUM];                    
  cout << hex << p1 << endl;                            //输出P1的地址
  //  delete[] p1;
  delete p1;
 
  T* p2 = new T[NUM];
  cout << p2 << endl;                                    //输出P2的地址
  delete[] p2;
  return 0;
}

(2)基本类型的对象没有析构函数,所以回收基本类型组成的数组空间用delete 和 delete[] 都是可以的;但是对于类对象数组,只能用delete[]。对于new的单个对象,只能用delete,不能用delete[]回收空间。

10.strcpy函数有什么缺陷,如何优化?

函数原型为char *strcpy(char *dest,const char *src);

函数说明:strcpy函数会将参数src字符串拷贝至参数dest所指的地址。

参数说明:dest,我们说的出参,最终得到的字符串。src,入参,因为其有const修饰。表示在此函数中不会也不能修改src的值。

返回值:返回dest字符串的起始地址。

附加说明:如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况。

==特别强调==:此函数很好用,可是它也很危险。如果在用的时候加上相关的长度判断,则会大大降低出此错误的危险。此函数还有一个特点,就是它在把字符串b拷贝到字符串a的时候,会在拷贝的a字符串的末尾加上一个\0结束标志。这个不同于strncpy()函数。

例如:

#include<string.h>

int main(){
    char a[30]="string(1)";
    char b[]="string(2)";
    
    printf("before strcpy():%s\n",a);

    if(strlen(b)<strlen(a)){
        printf("after strcpy():%s\n",strcpy(a,b));

    }
}