汇总专题01——C/C++常见面试知识点总结附面试真题 ——10.13

参考博文01:https://blog.csdn.net/hsq1596753614/article/details/80249605
博文02:https://blog.csdn.net/kuweicai/article/details/82779648

文章目录

博文01

【题目1】引用和指针有什么区别?

【解答】

(1)引用必须在定义时被初始化,指针可以不初始化;

(2)引用初始化后指向不可以改变;而指针的指向是可以改变的。

(3)不存在指向空值的引用,但存在指向空值的指针。

(4)定义一个指针变量编译器会为它分配内存,而引用不占用任何内存;

【题目2】函数参数传递中值传递、地址传递、引用传递有什么区别?

【解答】

(1) 值传递,会为形参重新分配内存空间,将实参的值拷贝给形参,形参的值不会影响实参的值,函数调用结束后形参被释放;

(2) 引用传递,不会为形参重新分配内存空间,形参只是实参的别名,形参的改变会影响实参的值,函数调用结束后形参不会被释放;

(3) 地址传递,形参为指针变量,将实参的地址传递给函数,可以在函数中改变实参的值,调用时为形参指针变量分配内存,结束时释放指针变量。

【题目3】static关键字有什么作用?

【解答】

(1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时维持上次的值;

(2)在模块内的static全局变量可以被模块内所用函数调用,但不能被模块外其他函数访问;

(3)在模块内的static函数只能被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内;

(4)在类中的static成员变量属于整个类拥有,对类的对象只有一份拷贝;

(5)在类中的static成员函数属于整个类拥有,这个函数不接收this指针,因而只能访问static成员变量。

【题目4】const关键字有什么作用?

【解答】

(1)阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它初始化,因为以后就没机会改变它了;

(2)对指针来说,可以指定指针本身为const,也可指定指针所指的数据为const,或二者同时指定为const;

(3) 在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;

(4) 对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;

(5) 对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为左值。

【题目5】链表和数组的区别在哪里?

【解答】

(1) 链表和数组都可以叫线性表,数组又叫顺序表,主要区别在于,顺序表是在内存中开辟一段连续的空间来存储数据,而链表是靠指针来连接多块不连续的空间,在逻辑上形成一片连续的空间来存储数据;

(2) 数组要求空间连续,占用总空间小,链表不要求空间连续,占用总空间大;

(3) 数组方便排序和查找,但删除和插入较慢;链表方便删除和插入,但查找较慢,不方便排序。

【题目6】请编写能直接实现strlen()函数功能的代码 经典!

【解答】

int strlen(char*str)

{

   for(int i=0;str[i]!=’\0’;i++);

return i;

}

【题目7】请编写能直接实现strstr()函数功能的代码

【解答】

char *strstr(char *str, const char *sub)

{

   char *p;

   for(int i=0;i<strlen(str);i++)

{

   p=&str[i];

   q=sub;

   if(*p==*q)

   {

          p++;

          q++;

          if(*q==’\0’)

                 return&str[i];

}

}

return null;

}

【题目8】进程和线程的差别

【解答】线程是指进程内的一个执行单元,也是进程内的可调度实体。线程与进程的区别:

(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位;

(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程也可以并发执行;

(3)拥有资源:进程是拥有资源的一个独立单元,线程不拥有系统资源但可以访问隶属于进程的资源;

(4)系统开销:在创建或撤销进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤销线程时的开销。

【题目9】const char * const p是什么意思?

【解答】

(1)char * constp;//常量指针,p的地址不可以修改

(2)char const*p;//指向常量的指针,指向的内容不可以改

(3)const char*p;//同char const *p

(4)const char *const p;//内容和地址都不可以改变

【题目10】memset、memcpy和strcpy的根本区别

【解答】

(1)memset用来对一段内存空间内全部设置为某个字符,一般用在对定义的字符串进行初始化为指定值;

(2)memcpy用来做内存拷贝,可以用来拷贝任何数据类型的对象,可以指定拷贝的数据长度;

(3)strcpy只能拷贝字符串,遇到’\0’就结束拷贝。

【题目11】析构函数有哪些特点?

【解答】

(1)析构函数也是特殊的类成员函数,它没有返回类型;

(2)没有参数;

(3)没有重载;

(4)public、private、protected等权限控制对析构函数无效;

(5)析构函数不能手动调用,只是在类对象生命周期结束的时候,由系统自动调用释放在构造函数中分配的资源。

【题目12】虚函数有什么作用?

【解答】

(1)虚函数的功能是使子类可以用同名的函数对父类函数进行覆盖,并且在通过父类指针调用时,如果有覆盖则自动调用子类覆盖函数,如果没有覆盖则调用父类中的函数,从而实现灵活扩展和多态性;

(2)如果是纯虚函数,则纯粹是为了在子类覆盖时有个统一的命名而已,子类必须覆盖纯虚函数,则否子类也是抽象类;

(3)含有纯虚函数的类称为抽象类,不能实例化对象,主要用作接口类。

【题目13】虚析构函数有什么作用? 见自己另外的博客

【解答】

(1)析构函数的工作方式是:最底层的派生类的析构函数最先被调用,然后调用每一个基类的析构函数;

(2)在C++中,当一个派生类对象通过使用一个基类指针删除,而这个基类有一个非虚的析构函数,则可能导致运行时派生类不能被销毁。然而基类部分很有可能已经被销毁,这就导致“部分析构”现象,造成内存泄漏;

(3)给基类一个虚析构函数,删除一个派生类对象的时候就将销毁整个对象,包括父类和全部的派生类部分。

【题目14】分别给出bool、int、float、指针变量与零值比较的if语句

【解答】

(1)bool型变量:if(!var)

(2)int型变量:if(var==0)

(3)float型变量:

const float EPSINON=0.000001;

if((x>=- EPSINON)&&(x<= EPSINON))

(4)指针变量:if(var==NULL)

【题目15】以下为Windows NT下32位C++程序,请计算sizeof的值

(1) void Func(charstr[100])

{

   sizeof(str)=?

}

(2) void *p=malloc(100);

sizeof§=?

(3) int a[100];

sizeof(a)=?

(4)char *p=”aaaaaaa”;

sizeof§=?

【解答】

(1) 4

(2) 4

(3) 400

(4) 4

【题目16】写一个函数返回1+2+3+…+n的值

【解答】

int Sum(int n)

{
       return ((long)1+n)*n/2;//或return (1+n)*n/2;

}
【题目17】深度遍历二叉树

【解答】

(1)深度优先搜索算法:沿着树的深度遍历树的节点,尽可能深地搜索树的分支;

(2)广度优先搜索算法:又叫宽度优先搜索,或横向优先搜索,是从根节点开始,沿着树的宽度遍历树的节点,如果所有节点均被访问则算法停止。

struct Node

{

   Node *Parent;

   Node *Left,*Right;

}

voidThrough(Node *Root)

{

   if(Root) printf(Root->data);

   if(Root->Left!=null)Through(Root->Left);//左

   if(Root->Right!=null)Through(Root-> Right);//右

}

【题目18】C++中的inline内联函数与普通函数的区别

【解答】

(1)所谓“内联函数”就是将很简单的函数内嵌到调用它的程序代码中,这样做的目的是节约原本函数调用时的时空开销,但作为内联函数,函数体必须十分简单,不能含有循环、条件、选择等复杂的结构;

(2)内联函数和宏的区别在于,**宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。**而且内联函数是真正的函数,只是在需要用到的时候内联函数像宏一样展开,所以取消了函数的参数压栈,减少了调用的开销。可以像调用函数一样调用内联函数,而不必担心会产生处理宏的一些问题。

(3)可以用inline来定义内联函数,不过,任何在类的说明部分定义的函数都会被自动的认为是内联函数。内联函数必须是和函数体声明在一起才有效。

【题目19】C++重写、重载、重定义的区别? 重要

【解答】

(1)成员函数重载特征:

a.相同的范围,在同一个类

b.函数名字相同

c.参数不同

(2)重写(覆盖)是指派生类函数覆盖基类函数,特征是: 虚函数重写是多态产生的三个条件之一

a.不同的范围,分别位于基类和派生类中

b.函数的名字相同

c.参数相同

d.基类函数必须有virtual关键字

(3)重定义(隐藏)是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

a.如果派生类的函数和基类的函数同名,但是参数不同,此时不管有无virtual,基类的函数被隐藏;

b.如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字,此时基类函数被隐藏。

【题目20】一个数据成员是否可以既是const又是static,如果不行,为什么?

【解答】

(1)一个数据成员可以既是const又是static,表示为静态常量;

(2)常量一般在构造函数后初始化;

(3)静态成员一般在类外初始化;

(4)静态常量在类外初始化,但要在类外初始化的同时声明为const。

【题目21】构造函数与析构函数的异同点

【解答】
相同点 :(1)都不能有返回类型;

1.构造函数有如下特点:

(1)构造函数的名字必须与类名相同;

(2)构造函数可以有任意类型的参数,但不能有返回类型;

(3)定义对象时,编译系统会自动调用构造函数;

(4)构造函数是特殊的成员函数,函数体可以在类体内也可以在类体外;

(5)构造函数被声明为公有函数,但它不能像其他成员函数那样被显式调用,它是在定义对象的同时被调用的。

2.析构函数有如下特点:

(1)析构函数的名字必须与类名相同,但它前面必须加一个波浪号;

(2)析构函数没有参数,也没有返回值,而且不能被重载,因此在一个类中只能有一个析构函数;

(3)当撤销对象时,编译系统会自动调用析构函数;

(4)析构函数可以是virtual,而构造函数不能是虚函数。

【题目22】自动调用复制构造函数(即拷贝构造函数)的几种情形

【解答】

1.复制构造函数的功能是用一个已知对象来初始化另一个同类的对象。复制构造函数其实也是类的构造函数,与类名相同,有且只有一个参数,是该类对象的引用;每个类必须有一个复制构造函数。如果定义类的时候没有编写,编译器编译时会自动生成一个复制构造函数。

2.复制构造函数在三种情况下会自动被调用:

(1)当类的一个对象去初始化该类的另一个对象时;

(2)如果函数的形参是类的对象,调用函数进行形参和实参结合时;

(3)如果函数的返回值是类对象,函数调用完成返回时,会产生一个临时对象。

【题目23】类型转换构造函数是什么?举个例子。

【解答】

类型转换构造函数就是自动调用类型匹配的构造函数,自动将基本数据类型转换成对象。例子如下:

#include<iostream>

using namespacestd;

class Person{

public:

     double height;

     Person(double h){height=h;}

};

void main()

{

       Person yaoming=2.3;  //会自动根据初始值匹配到相应的构造函数

       cout<<yaoming.height<<endl;

}
【题目24】简述C++异常处理方式

【解答】 catch —》 throw----》try

一个典型的C++异常处理包含以下几个步骤:

(1)程序执行时发生错误;

(2)以一个异常对象(最简单是一个整数)记录错误的原因及相关信息;

(3)程序监测到这个错误(读取异常对象);

(4)程序决定如何处理错误;

(5)进行错误处理,并在此后恢复/终止程序的执行。

【题目25】成员函数和友元函数的区别

【解答】

(1)成员函数是类定义的一部分,通过特定的对象来调用。成员函数既可以隐式访问调用对象的成员,而无须使用成员操作符;

(2)友元函数不是类的组成部分,因此被称为直接函数调用。友元函数不能隐式访问类成员,而必须将成员操作符用于作为参数传递的对象。

【题目26】C++中哪些运算符不可以重载?

【解答】

不能重载的5个运算符:

(1) . 直接成员运算符

(2) ?: 条件运算符

(3) sizeof 字节计算运算符

(4) ::

(5) *

【题目27】如何重载前++和后++运算符?

【解答】

前++不带参数,后++带一个int型参数以示区分。

iCount &operator ++()//前缀++

{

   cout<<”前缀++”<<endl;

   m_data++;

   return *this;

}

iCount &operator ++(int)//后置++

{

   cout<<”前缀++”<<endl;

   iCount temp=*this;

   m_data++;

   return temp;

}

【题目28】请说出STL标准模板库中的几个常用类?

【解答】

数据结构描述实现头文件
向量(vector)连续存储的元素vector
列表(list)由节点组成的双向链表list
集合(set)由节点组成的集合set
栈(stack)后进先出的值的排列stack
队列(queue)先进先出的值的排列queue
映射(map)由{键,值}对组成的集合map

在这里插入图片描述

【题目29】函数模板与函数重载的异同?

【解答】

(1)函数的重载是指定义了几个名字相同,但参数的类型或参数的个数不同的函数;

(2)模板函数是指的几个函数的具体算法相同,而参数类型不同的函数;

(3)模板函数可以减少重载函数,但也可能引发错误。

【题目30】类型转换构造函数是什么?

【解答】

类型转换构造函数就是自动调用类型匹配的构造函数,自动将基本数据类型转换成对象。

【题目31】C++中explicit关键字有什么作用?

【解答】

explicit和构造函数一起使用,explicit指明构造函数只能显式使用,目的是为了防止不必要的隐式调用类型转换构造函数。

#include

using namespacestd;

class Person{

   int height;

public:

   Person(){}   

explicit Person(int h)// explicit修饰构造函数

{

   height=h;

}

void display()

{

   cout<<height<<endl;

}

};

void main()

{

//Person yaoming=2.3;//构造函数用explici修饰后将不能隐式调用类型转换构

//造函数

Person yaoming2(5);//可以显式调用类型构造函数

}

【题目32】C++中restrict关键字有什么作用?

【解答】

(1)restrict是用来优化的;

(2)restrict只能修饰指针,restrict修饰的指针是能够访问所指区域的唯一入口,限制多个指针指向同一地址。

【题目33】C++中常用的设计模式有哪些?

【解答】共有23种设计模式,但真正在开发中常用的模式有:

(1)Factory Method(工厂模式);

(2)Strategy(策略模式);

(3)Singleton(单例模式);

(4)Iterator(迭代器模式);

(5)Abstract Factory(抽象工厂模式);

(6)Builder(建造者模式);

(7)Adapter(适配器模式);

(8)Bridge(桥接模式);

(9)Composite(组合模式);

(10)Interpreter(解释器模式);

(11)Command(命令模式);

(12)Mediator(中介者模式);

(13)Observer(观察者模式);

(14)State(状态模式);

(15)Proxy(代理模式)。

【题目34】编写一个单例模式的例子

【解答】单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。该实例被所有程序模块共享。

#include

using namespacestd;

classCSingleton

{

private:

   CSingleton(){}//构造函数是私有的

   static CSingleton *m_pInstance;// static

public:

   static CSingleton *GetInstance()//static

   {

   if(m_pInstance==NULL)//判断是否第一次调用

          m_pInstance=newCSingleton();

   return m_pInstance;

}

};

CSingleton * CSingleton::m_pInstance=NULL;//static属性类外初始化

void main()

{

   CSingleton *p1= CSingleton:: GetInstance();

   CSingleton *p2= CSingleton:: GetInstance();

   cout<<(p1==p2)<<endl;//结果为true表示单例

}

【题目35】面向对象的三大特征是什么?

【解答】面向对象的三个基本特征:封装、继承、多态。

【题目36】什么是封装?

【解答】封装的两层含义

(1)把属性和方法进行封装
(2)对属性和方法进行访问控制

【题目36】什么是封装?

【解答】

(1)封装是面向对象的特征之一,是对象和类概念的主要特性;

(2)封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏;

(3)在C++中类中成员的属性有:public、protected、private,这三个属性的访问权限依次降低。

【题目37】什么是继承?

【解答】

(1)继承是指:可以使用现有类的所有功能,并在无须重新编写原来的类的情况下对这些功能进行扩展;

(2)通过继承创建的类称为“子类”或“派生类”;

(3)被继承的类称为“基类”、“父类”或“超类”;

(4)在某些OOP语言中,一个子类可以继承多个基类,但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现;

(5)C++中可以用public、protected、private来修饰继承特性。

【题目38】多态实现的三个条件?

【解答】

(1)要有继承
(2)有虚函数重写 ,vitual 重写
(3)父类指针指针(引用)指向子类对象

【题目38】多态的实现效果?

【解答】

多态:同样的调用语句有多种不同的变现形式。
也可以说成一种 接口,多种方法(函数)。
传智扫地僧老师说过:多态就是一个函数(自己搭建的那个舞台)在子类中穿梭的时候表现出的不同的形态。

【题目38】什么是多态?

【解答】

(1)多态性:允许将父类对象设置为和一个或更多的它的子对象相等的技术,赋值之后,父对象可以根据当前赋值给它的子对象的特性以不同的方式运作。简单地说,允许将子类类型的指针赋值给父类型的指针;

(2)实现多态的两种方式:重写(覆盖)、重载;

(3)重写(覆盖)覆盖:子类重新定义父类的虚函数;

(4)重载:允许存在多个同名函数,而这些函数的参数表不同。

【题目39】类与对象的区别?

【解答】

类与对象的区别,如人类与张三的区别,它们是一般与个体、抽象与具体、集体与个体的区别。

【题目40】C++中namespace是什么?

【解答】

namespace命名空间,是C++的语言特性,类似于Java中的包。

【题目41】什么是可重入和不可重入函数?

【解答】

(1)什么是可重入性?

可重入函数可以由多个任务并发使用,而不必担心数据错误。相反,不可重入函数不能由多个任务共享,除非能确保函数的互斥。可重入函数可以在任意时刻被中断,稍后继续运行,不会丢失数据,可重入函数要么使用本地变量,要么使用全局变量时保护自己的数据。

(2)可重入函数

不可连续的调用持有静态数据;

不返回指向静态数据的指针,所有数据都是由函数的调用者提供;

使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据;

如果必须访问全局变量,记住利用互斥信号量来保护全局变量;

绝不调用任何不可重入函数。

(3)不可重入函数

函数中使用了静态变量,无论是全局静态变量还是局部静态变量;

函数返回静态变量;

函数中调用了不可重入函数;

函数体内使用了静态的数据结构;

函数体内调用了malloc()或者free()函数;

函数体内调用了其他标准I/O函数;

总之,如果一个函数在重入条件下使用了未受保护的共享资源,那么就是不可重入的。

博文02

第一部分:计算机基础

1. C/C++内存有哪几种类型?

C中,内存四区模型:堆(malloc)、栈(如局部变量、函数参数也属于局部变量)、程序代码区(存放二进制代码)、全局/静态存储区(全局变量、static变量)和常量存储区(常量)。

全局变量、static变量会初始化为零,而堆和栈上的变量是随机的,不确定的。

2. 堆和栈的区别?

1).堆存放动态分配的对象——即那些在程序运行时分配的对象,比如局部变量,其生存期由程序控制;
2).栈用来保存定义在函数内的非static对象,仅在其定义的程序块运行时才存在;
3).静态内存用来保存static对象,类static数据成员以及定义在任何函数外部的变量,static对象在使用之前分配,程序结束时销毁;
4).栈和静态内存的对象由编译器自动创建和销毁。

3. 堆和自由存储区的区别?

总的来说,堆是C语言和操作系统的术语,是操作系统维护的一块动态分配内存;自由存储是C++中通过new与delete动态分配和释放对象的抽象概念。他们并不是完全一样。
从技术上来说,堆(heap)是C语言和操作系统的术语。堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,稍后调用free可把内存交还。而自由存储是C++中通过new和delete动态分配和释放对象的抽象概念,通过new来申请的内存区域可称为自由存储区。基本上,所有的C++编译器默认使用堆来实现自由存储,也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,这时后由new运算符分配的对象,说它在堆上也对,说它在自由存储区上也正确。

4. 程序编译的过程?

程序编译的过程中就是将用户的文本形式的源代码(c/c++)转化成计算机可以直接执行的机器代码的过程。主要经过四个过程:(1)预处理、(2)编译、(3)汇编和(4)链接。具体示例如下。
一个hello.c的c语言程序如下。

#include <stdio.h>
int main()
{
    printf("happy new year!\n");
    return 0;
}

其编译过程如下:
在这里插入图片描述

5. 计算机内部如何存储负数和浮点数?

负数比较容易,就是通过一个标志位和补码来表示。
对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit,double数据占用64bit,我们在声明一个变量float f= 2.25f的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是float还是double在存储方式上都是遵从IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53。更多可以参考浮点数表示。
无论是单精度还是双精度在存储中都分为三个部分:

1). 符号位(Sign) : 0代表正,1代表为负
2). 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储
3). 尾数部分(Mantissa):尾数部分
其中float的存储方式如下图所示:
在这里插入图片描述
而双精度的存储方式如下图:
在这里插入图片描述

6. 函数调用的过程?

如下结构的代码,

int main(void)
{
  ...
  d = fun(a, b, c);
  cout<<d<<endl;
  ...
  return 0;
}

调用fun()的过程大致如下:

main()========
1).参数拷贝(压栈),注意顺序是从右到左,即c-b-a;
2).保存d = fun(a, b, c)的下一条指令,即cout<<d<<endl(实际上是这条语句对应的汇编指令的起始位置);
3).跳转到fun()函数,注意,到目前为止,这些都是在main()中进行的;
fun()=====
4).移动ebp、esp形成新的栈帧结构;
5).压栈(push)形成临时变量并执行相关操作;
6).return一个值;
7).出栈(pop);
8).恢复main函数的栈帧结构;
9).返回main函数;
main()========
。。。

7. 左值和右值

不是很严谨的来说,左值指的是既能够出现在等号左边也能出现在等号右边的变量(或表达式),右值指的则是只能出现在等号右边的变量(或表达式)。
举例来说我们定义的变量 a 就是一个左值,而malloc返回的就是一个右值。或者左值就是在程序中能够寻值的东西,右值就是一个具体的真实的值或者对象,没法取到它的地址的东西(不完全准确),因此没法对右值进行赋值,但是右值并非是不可修改的,比如自己定义的class, 可以通过它的成员函数来修改右值。

8. 什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?你通常采用哪些方法来避免和减少这类错误?

用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。

1). 使用的时候要记得指针的长度.
2). malloc的时候得确定在那里free.
3). 对指针赋值的时候应该注意被赋值指针需要不需要释放.
4). 动态分配内存的指针最好不要再次赋值.
5). 在C++中应该优先考虑使用智能指针.

第二部分:C v.s. C++

1. C和C++的区别?

1). C++是C的超集;
2). C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制),而对于C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。

2. int fun() 和 int fun(void)的区别?

这里考察的是c 中的默认类型机制。

在c中,int fun() 会解读为返回值为int(即使前面没有int,也是如此,但是在c++中如果没有返回类型将报错),输入类型和个数没有限制, 而int fun(void)则限制输入类型为一个void。

在c++下,这两种情况都会解读为返回int类型,输入void类型。

3. const 有什么用途

主要有三点:

1).定义只读变量,或者常量(只读变量和常量的区别参考下面一条);
2).修饰函数的参数和函数的返回值;
3).修饰函数的定义体,这里的函数为类的成员函数,被const修饰的成员函数代表不能修改成员变量的值,因此const成员函数只能调用const成员函数;
4).只读对象。只读对象只能调用const成员函数。
class Screen {
public:
const char cha; //const成员变量
char get() const; //const成员函数
};

const Screen screen; //只读对象

4. 在C中用const 能定义真正意义上的常量吗?C++中的const呢?

不能。c中的const仅仅是从编译层来限定,不允许对const 变量进行赋值操作,在运行期是无效的,所以并非是真正的常量(比如通过指针对const变量是可以修改值的),但是c++中是有区别的,c++在编译时会把const常量加入符号表,以后(仍然在编译期)遇到这个变量会从符号表中查找,所以在C++中是不可能修改到const变量的。
补充:

1). c中的局部const常量存储在栈空间,全局const常量存在只读存储区,所以全局const常量也是无法修改的,它是一个只读变量。
2). 这里需要说明的是,常量并非仅仅是不可修改,而是相对于变量,它的值在编译期已经决定,而不是在运行时决定。
3).c++中的const 和宏定义是有区别的,宏是在预编译期直接进行文本替换,而const发生在编译期,是可以进行类型检查和作用域检查的。
4).c语言中只有enum可以实现真正的常量。
5). c++中只有用字面量初始化的const常量会被加入符号表,而变量初始化的const常量依然只是只读变量。
6). c++中const成员为只读变量,可以通过指针修改const成员的值,另外const成员变量只能在初始化列表中进行初始化。

下面我们通过代码来看看区别。
同样一段代码,在c编译器下,打印结果为*pa = 4, 4
在c++编译下打印的结果为 *pa = 4, 8

int main(void)
{
const int a = 8;
int *pa = (int *)&a;
*pa = 4;
printf("*pa = %d, a = %d", *pa, a);
return 0;
}

另外值得一说的是,由于c++中const常量的值在编译期就已经决定,下面的做法是OK的,但是c中是编译通不过的。

int main(void)
{
const int a = 8;
const int b = 2;
int array[a+b] = {0};
return 0;
}

5. 宏和内联(inline)函数的比较?

1). 首先宏是C中引入的一种预处理功能,宏是在预处理阶段
2). 内联(inline)函数是C++中引用的一个新的关键字;C++中推荐使用内联函数来替代宏代码片段;
3). 内联函数将函数体直接扩展到调用内联函数的地方,这样减少了参数压栈,跳转,返回等过程;
4). 由于**内联发生在编译阶段****,所以内联相较宏,是有参数检查和返回值检查的,因此使用起来更为安全;
5). 需要注意的是, inline会向编译期提出内联请求,但是是否内联由编译期决定(当然可以通过设置编译器,强制使用内联);
6). 由于内联是一种优化方式,在某些情况下,即使没有显示的声明内联,比如定义在class内部的方法,编译器也可能将其作为内联函数。
7). 内联函数不能过于复杂,最初C++限定不能有任何形式的循环,不能有过多的条件判断,不能对函数进行取地址操作等,但是现在的编译器几乎没有什么限制,基本都可以实现内联。
更多请参考inline关键字

6. C++中有了malloc / free , 为什么还需要 new / delete?

1). malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
2). 对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete是操作符,不是库函数。
最后补充一点题外话,new 在申请内存的时候就可以初始化(如下代码), 而malloc是不允许的。另外,由于malloc是库函数,需要相应的库支持,因此某些简易的平台可能不支持,但是new就没有这个问题了,因为new是C++语言所自带的运算符。
int *p = new int(1);
1
特别的,在C++中,如下的代码,用new创建一个对象(new 会触发构造函数, delete会触发析构函数),但是malloc仅仅申请了一个空间,所以在C++中引入new和delete来支持面向对象。

#include
class Test
{

}

Test* pn = new Test;
Test* pm = (Test*)malloc(sizeof(Test));

7. C和C++中的强制类型转换?

C中是直接在变量或者表达式前面加上(小括号括起来的)目标类型来进行转换,一招走天下,操作简单,但是由于太过直接,缺少检查,因此容易发生编译检查不到错误,而人工检查又及其难以发现的情况;而C++中引入了下面四种转换:

1). static_cast
a. 用于基本类型间的转换
b. 不能用于基本类型指针间的转换
c. 用于有继承关系类对象间的转换和类指针间的转换
2). dynamic_cast
a. 用于有继承关系的类指针间的转换
b. 用于有交叉关系的类指针间的转换
c. 具有类型检查的功能
d. 需要虚函数的支持
3). reinterpret_cast
a. 用于指针间的类型转换
b. 用于整数和指针间的类型转换
4). const_cast
a. 用于去掉变量的const属性
b. 转换的目标类型必须是指针或者引用
在C++中,普通类型可以通过类型转换构造函数转换为类类型,那么类可以转换为普通类型吗?答案是肯定的。但是在工程应用中一般不用类型转换函数,因为无法抑制隐式的调用类型转换函数(类型转换构造函数可以通过explicit来抑制其被隐式的调用),而隐式调用经常是bug的来源。实际工程中替代的方式是定义一个普通函数,通过显式的调用来达到类型转换的目的。
class test{
int m_value;

public:
operator int() //类型转换函数
{
return m_value;
}

int toInt() //显示调用普通函数来实现类型转换
{
    return m_value
}

};

int main()
{

test a(5);
int i = a;

return 0;

}

8. static 有什么用途

1). 静态(局部/全局)变量
2). 静态函数
3). 类的静态数据成员
4). 类的静态成员函数

9. 类的静态成员变量和静态成员函数各有哪些特性?

静态成员变量
1). 静态成员变量需要在类内声明(加static),在类外初始化(不能加static),如下例所示;
2). 静态成员变量在类外单独分配存储空间,位于全局数据区,因此静态成员变量的生命周期不依赖于类的某个对象,而是所有类的对象共享静态成员变量;
3). 可以通过对象名直接访问公有静态成员变量;
4). 可以通过类名直接调用公有静态成员变量,即不需要通过对象,这一点是普通成员变量所不具备的。
class example{
private:
static int m_int; //static成员变量
};

int example::m_int = 0; //没有static

cout<<example::m_int; //可以直接通过类名调用静态成员变量

静态成员函数
1). 静态成员函数是类所共享的;
2). 静态成员函数可以访问静态成员变量,但是不能直接访问普通成员变量(需要通过对象来访问);需要注意的是普通成员函数既可以访问普通成员变量,也可以访问静态成员变量;
3). 可以通过对象名直接访问公有静态成员函数;
4). 可以通过类名直接调用公有静态成员函数,即不需要通过对象,这一点是普通成员函数所不具备的。
class example{
private:
static int m_int_s; //static成员变量
int m_int;
static int getI() //静态成员函数在普通成员函数前加static即可
{
return m_int_s; //如果返回m_int则报错,但是可以return d.m_int是合法的
}
};

cout<<example::getI(); //可以直接通过类名调用静态成员变量

10. 在C++程序中调用被C编译器编译后的函数,为什么要加extern“C”?

C++语言支持函数重载,C语言不支持函数重载,函数被C++编译器编译后在库中的名字与C语言的不同,假设某个函数原型为:

      void foo(int x, int y);

该函数被C编译器编译后在库中的名字为 _foo, 而C++编译器则会产生像: _foo_int_int 之类的名字。为了解决此类名字匹配的问题,C++提供了C链接交换指定符号 extern “C”。

11. 头文件中的 ifndef/define/endif 是干什么用的? 该用法和 program once 的区别?

相同点:
它们的作用是防止头文件被重复包含。
不同点

1). ifndef 由语言本身提供支持,但是 program once 一般由编译器提供支持,也就是说,有可能出现编译器不支持的情况(主要是比较老的编译器)。
2). 通常运行速度上 ifndef 一般慢于 program once,特别是在大型项目上, 区别会比较明显,所以越来越多的编译器开始支持 program once。
3). ifndef 作用于某一段被包含(define 和 endif 之间)的代码, 而 program once 则是针对包含该语句的文件, 这也是为什么 program once 速度更快的原因。
4). 如果用 ifndef 包含某一段宏定义,当这个宏名字出现“撞车”时,可能会出现这个宏在程序中提示宏未定义的情况(在编写大型程序时特性需要注意,因为有很多程序员在同时写代码)。相反由于program once 针对整个文件, 因此它不存在宏名字“撞车”的情况, 但是如果某个头文件被多次拷贝,program once 无法保证不被多次包含,因为program once 是从物理上判断是不是同一个头文件,而不是从内容上。

12. 当i是一个整数的时候++i和i++那个更快一点?i++和++i的区别是什么?

答:理论上++i更快,实际与编译器优化有关,通常几乎无差别。

//i++实现代码为:
int operator++(int)
{
    int temp = *this;
    ++*this;
    return temp;
}//返回一个int型的对象本身

// ++i实现代码为:
int& operator++()
{
    *this += 1;
    return *this;
}//返回一个int型的对象引用

i++和++i的考点比较多,简单来说,就是i++返回的是i的值,而++i返回的是i+1的值。也就是++i是一个确定的值,是一个可修改的左值,如下使用:

cout << ++(++(++i)) << endl;
cout << ++ ++i << endl;

可以不停的嵌套++i。
这里有很多的经典笔试题,一起来观摩下:

int main()
{
int i = 1;
printf("%d,%d\n", ++i, ++i); //3,3
printf("%d,%d\n", ++i, i++); //5,3
printf("%d,%d\n", i++, i++); //6,5
printf("%d,%d\n", i++, ++i); //8,9
system(“pause”);
return 0;
}

首先是函数的参数入栈顺序从右向左入栈的,计算顺序也是从右往左计算的,不过都是计算完以后再进行的压栈操作:
对于第1个printf,首先执行++i,返回值是i,这时i的值是2,再次执行++i,返回值是i,得到i=3,将i压入栈中,此时i为3,也就是压入3,3;
对于第2个printf,首先执行i++,返回值是原来的i,也就是3,再执行++i,返回值是i,依次将3,5压入栈中得到输出结果
对于第3个printf,首先执行i++,返回值是5,再执行i++返回值是6,依次将5,6压入栈中得到输出结果
对于第4个printf,首先执行++i,返回i,此时i为8,再执行i++,返回值是8,此时i为9,依次将i,8也就是9,8压入栈中,得到输出结果。
上面的分析也是基于VS搞的,不过准确来说函数多个参数的计算顺序是未定义的(the order of evaluation of function arguments are undefined)。笔试题目的运行结果随不同的编译器而异。

第三部分:数组、指针 & 引用

1. 指针和引用的区别?

相同点:

1). 都是地址的概念;
2). 都是“指向”一块内存。指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名;
3). 引用在内部实现其实是借助指针来实现的,一些场合下引用可以替代指针,比如作为函数形参。
不同点:
1). 指针是一个实体,而引用(看起来,这点很重要)仅是个别名; 2. 引用占用内存空间吗?
2). 引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可以“见异思迁”;
3). 引用不能为空,指针可以为空;
4). “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
5). 指针和引用的自增(++)运算意义不一样;
6). 引用是类型安全的,而指针不是 (引用比指针多了类型检查)
7). 引用具有更好的可读性和实用性。

2. 引用占用内存空间吗?

如下代码中对引用取地址,其实是取的引用所对应的内存空间的地址。这个现象让人觉得引用好像并非一个实体。但是引用是占用内存空间的,而且其占用的内存和指针一样,因为引用的内部实现就是通过指针来完成的。

比如 Type& name; <===> Type* const name。

int main(void)
{
        int a = 8;
        const int &b = a;
        int *p = &a;
        *p = 0;
        cout<<a; //output 0
    return 0;
}
3. 三目运算符

在C中三目运算符(? :)的结果仅仅可以作为右值,比如如下的做法在C编译器下是会报错的,但是C++中却是可以是通过的。这个进步就是通过引用来实现的,因为下面的三目运算符的返回结果是一个引用,然后对引用进行赋值是允许的。

int main(void)
{
int a = 8;
int b = 6;
(a>b ? a : b) = 88;
cout<<a; //output 88
return 0;
}

4. 指针数组和数组指针的区别

数组指针,是指向数组的指针,而指针数组则是指该数组的元素均为指针。

数组指针,是指向数组的指针,其本质为指针,形式如下。如 int (*p)[10],p即为指向数组的指针,()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。数组指针是指向数组首元素的地址的指针,其本质为指针,可以看成是二级指针。
类型名 (*数组标识符)[数组长度]

指针数组,在C语言和C++中,数组元素全为指针的数组称为指针数组,其中一维指针数组的定义形式如下。指针数组中每一个元素均为指针,其本质为数组。如 int p[n], []优先级高,先与p结合成为一个数组,再由int说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 p=a; 这里p表示指针数组第一个元素的值,a的首地址的值。
类型名 *数组标识符[数组长度]
1

第四部分:C++特性

1. 什么是面向对象(OOP)?面向对象的意义?

Object Oriented Programming, 面向对象是一种对现实世界理解和抽象的方法、思想,通过将需求要素转化为对象进行问题处理的一种思想。其核心思想是数据抽象、继承和动态绑定(多态)。
面向对象的意义在于:将日常生活中习惯的思维方式引入程序设计中;将需求中的概念直观的映射到解决方案中;以模块为中心构建可复用的软件系统;提高软件产品的可维护性和可扩展性。

2. 解释下封装、继承和多态?

1). 封装:
封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。
封装的意义在于保护或者防止代码(数据)被我们无意中破坏。
2). 继承:
继承主要实现重用代码,节省开发时间。
子类可以继承父类的一些东西。
a. 公有继承(public)
公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问。
b. 私有继承(private)
私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。
c. 保护继承(protected)
保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。

3. 什么时候生成默认构造函数(无参构造函数)?什么时候生成默认拷贝构造函数?什么是深拷贝?什么是浅拷贝?默认拷贝构造函数是哪种拷贝?什么时候用深拷贝?

1). 没有任何构造函数时,编译器会自动生成默认构造函数,会生成无参构造函数;当类没有拷贝构造函数时,会生成默认拷贝构造函数。
2). 深拷贝是指拷贝后对象的逻辑状态相同,而浅拷贝是指拷贝后对象的物理状态相同;默认拷贝构造函数属于浅拷贝。
3). 当系统中有成员指代了系统中的资源时,需要深拷贝。比如指向了动态内存空间,打开了外存中的文件或者使用了系统中的网络接口等。如果不进行深拷贝,比如动态内存空间,可能会出现多次被释放的问题。是否需要定义拷贝构造函数的原则是,是类是否有成员调用了系统资源,如果定义拷贝构造函数,一定是定义深拷贝,否则没有意义。

4. 构造函数和析构函数的执行顺序?

构造函数: “没有父哪有子”
1). 首先调用父类的构造函数;
2). 调用成员变量的构造函数;
3). 调用类自身的构造函数。
析构函数
对于栈对象或者全局对象,调用顺序与构造函数的调用顺序刚好相反,也即后构造的先析构。
对于堆对象,析构顺序与delete的顺序相关。???不懂

5. C++的编译环境

如下图所示,C++的编译环境由如下四部分构成:(1)C++标准库、(2)C语言兼容库、(2)编译器扩展库 以及(4)编译模块。
在这里插入图片描述

#include<iostream>  //C++标准库,不带".h"
#include<string.h>  //C语言兼容库,由编译器厂商提供

值得注意的是,C语言兼容库功能上跟C++标准库中的C语言子库相同,它的存中主要为了兼容C语言编译器,也就是说如果一个文件只包含C语言兼容库(不包含C++标准库),那么它在C语言编译器中依然可以编译通过。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值