C++面试题

1、C++和C的区别

思想:C++面向对象   C面向过程的结构化语言

语法:C++具有封装、继承、多态三大特性

           C++增加更多类型安全的功能,如强制类型转换

           C++支持范式编程(模板)

2、static关键字的作用

(1)全局静态变量

在全局变量前加上关键字static,全局变量就被定义成一个全局静态变量;

存储在静态存储区,在整个程序运行期间一直存在; 

未初始化的全局静态变量会自动初始化为0;

作用域为声明static的文件,文件外不可访问。

(2)局部静态变量

存储在静态存储区;

未初始化的全局静态变量会自动初始化为0;

作用域为函数体内,函数结束,变量失效,但是并没有销毁,重新调用函数时,仍然可以调用static变量,值不变。

(3)静态函数

在函数返回类型前加上static,函数就定义为静态函数。函数的定义和声明默认是extern类型的,但静态函数只能在声明它的文件中使用(如果函数前加上static,就不会同其他文件中的同名函数冲突);

注意:不要在头文件中声明静态全局函数;

          不要在cpp文件中声明非static的全局函数

(4)类的静态成员

静态成员可以实现多个对象之间的数据共享,还不会破坏封装特性,因此静态成员是类的静态成员,而非对象的静态成员。

(5)类的静态成员函数

类的静态的成员函数是属于类的,不属于某一的对象,因此引用格式:<类名>::<静态成员函数名>(参数列表)。在静态成员函数的实现中不能直接引用类中说明的非静态成员。

3、C++中四种 cast 转换

(1)const_cast。用于将const变量转为非const。

(2)static_cast。用于各种隐式转化。例如 非const 转 const , void* 转为指针,static_cast能用于向上转化。

(3)dynamic_cast。动态类型转换。只能用于含有虚函数的类,用于层次间的向上(子类向基类的转换)或向下转化(基类向子类的转换)。

(4)reinterpret_cast。

另外:C的强制类型转换,转换的不明确,不能进行错误检查,容易出错。

4、内存分区

栈区:就是由编译器在需要时分配,不需要的时候自动清除的变量的存储区,通常存储局部变量、函数参数、返回值等

堆区:容量远远大于栈。由程序员申请并负责释放。

堆栈的区别:

(1)管理方式不同。栈区由系统管理,堆区由程序员管理,malloc()和free()、new()和delete(),应该是成对存在的,否则将会产生内存泄漏

(2)空间大小不同:

(3)碎片问题:对于堆,频繁的new()、delete()势必造成内存空间的不连续,从而造成大量的碎片,降低系统效率。栈区则不会有这种问题。

(4)生长方向:堆区,由低地址向高地址生长;栈区,由高地址向低地址生长。

(5)分配方式:栈区,可以静态分配,也可以动态分配;堆区都是动态分配。

(6)分配效率:栈分配比堆分配的效率更高

mmap:映射区

内存映射就是将用户空间的一段内存区域映射到内核空间,映射成功后,用户对这段内存区域的修改就会直接反映到内核空间。内核空间对内存的修改也会直接反映到用户空间。

具体参考:Linux内存映射mmap原理分析

5、C++中struct和class的区别

(1)默认访问属性不同。struct默认是public,而class是private的。

(2)在继承关系中的默认访问属性不同。在继承关系中,struct默认是public的,而class是private的。默认的访问属性取决于子类,而非基类。例如:类B默认继承结构体A,它的访问属性就是私有的;结构体B默认继承类A,那么他的默认继承属性就是公有的。为避免访问属性不明确的情况,在继承时应当显式指定继承方式。

(3)class可用于模板。

6、计算机中运算优先级:算法运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符

7、C++中的深拷贝与浅拷贝

拷贝构造函数是一种特殊的构造函数,它的任务就是拷贝对象。

默认的拷贝构造函数。当程序中没有自定义的拷贝函数时,系统自动提供一个缺省的拷贝构造函数,这个函数的访问属性是公有的。其有两种拷贝方式:

(1)普通成员(内置类型,指针等)按位拷贝,执行浅拷贝;

(2)对象成员,执行其拷贝函数,具体行为由拷贝构造函数决定。

浅拷贝:简单的复制拷贝操作。将源对象的值拷贝到目标对象中去,本质上源对象与目标对象共用一份实体,只是变量名不同,地址还是相同的。拷贝构造函数默认的是浅拷贝,只要不涉及到堆内存,浅拷贝完全可以。

//浅拷贝
#include <iostream>
using namespace std;

class Student
{
private:
	int* m_hight; //身高
	int m_age;
public:
	Student(int hight,int age)
	{
		m_hight = new int(hight);
		m_age = age;
		cout << "Student构造函数" << endl;
	}
	~Student()
	{
		if (m_hight != NULL)
		{//堆区内存程序员开辟,由程序员释放
			delete m_hight;
		}
		m_hight = NULL;    //防止野指针的出现
		cout << "Student 析构函数" << endl;
	}
};

int main()
{
	Student p1(180,23);
	Student p2(p1);
	return 0;
}

运行这个程序,在执行到第二次析构函数的时候,程序崩溃了,此时使用深拷贝就可以解决问题。

深拷贝:在堆区重新申请空间,进行拷贝操作。拷贝的时候先开辟出和源对象大小一样的空间,然后将源对象里的内容拷贝到目标对象中去,这样两个指针就指定了不同的内存位置,并且里边的内容也是一样的。这样两个指针先后去调用析构函数,分别释放自己指向的位置,就不会产生重复释放同一块内存的错误了。

//自定义的拷贝构造函数,实现深拷贝,如果不自己写,系统就会使用缺省的拷贝构造,就可能会出现程序崩溃的情况
//自己实现拷贝构造
	Student(const Student& p)
	{
		cout << "拷贝构造" << endl;

		//编译器默认此种拷贝方式
		m_age = p.m_age;    // 拷贝成功
//		m_hight = p.m_hight;   // 这行就会产生问题,注释掉

		//深拷贝操作
		m_hight = new int( * p.m_hight); //重新申请空间
	}

8、C++中指针和引用的区别

(1)指针有自己的空间,引用只是一个别名;

(2)对于sizeof操作,指针的大小是4(32位),引用则是被引用对象的大小;

(3)指针可以被初始化为NULL,引用必须是一个已有对象的引用;

(4)指针在使用中可以指向其他对象,但是引用只能是一个对象的引用,不能改变;

(5)指针可以有多级指针,而引用只有一级;

(6)如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露;

(7)引用定义时必须初始化;

以下为扩展:

【引用 reference】

引用更接近 const指针,必须在创建时进行初始化,一旦与某个变量关联起来,就一直效忠于它。即  int&  val = num;   实际上就是   int * const val = &num;

程序示例来自C++ Primer Plus 中文第六版P257。 

rodents = bunnies; 试图改变rodents引用,依据理论知识,这必然是失败的,从程序运行结果来看rodents的地址依然是rats的地址,对rodents值的改变也反应到了rats上。

引用可以作为函数参数void swap_int(int &a,int &b)、引用类型的函数(返回对象的引用)、常引用(const int& val= num;保护原值不受更新)、多态(父类引用指向子类对象)

//返回对象的引用,效率更高
struct free_throws {};
free_throws& accumulate(free_throws& target, const free_throws& source)
{
    return target;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值