C/C++面试问题记录

本文详细讲解了堆、栈的区别,new和malloc的差异,static、const和extern的用法,指针和引用的对比,以及内存对齐、封装、继承和多态的概念,帮助读者掌握C++中关键概念和技术细节。
摘要由CSDN通过智能技术生成

1. 堆和栈的区别?

首先介绍什么是堆、栈

堆是用来存储动态分配对象的一块内存,即程序在运行时动态分配的空间,malloc 函数或者new动态开辟的内存

栈是用来存储定义在函数内的,非static的对象,如局部变量、函数参数,他在首次运行到时生成存放在栈中,在程序块结束时消亡,即只有在程序块运行时才存在

static修饰的对象是存在静态存储区或者全局遍历区,static对象在使用之前分配,在整个程序结束后消亡

栈和静态内存的创建是编译器自己创建的,堆则需要人手动的创建和释放

2.new和malloc的区别

属性方面:

new是C++中的关键字,需要编译器支持即可,而malloc是库函数,调用时需要包含库函数头文件,因此new的效率更高

#include<malloc.h>

参数方面:

new开辟空间时不需要指定字节大小的参数,而malloc需要指定开辟 空间大小的参数

int *p = new int[100]   //生成100个int大小的内存空间

int *p = (int)malloc(sizeof(int)100)  // 生成100个int大小的内存空间

int *p = new int(6)  //分配大小为sizeof(int)的空间,并且初始化为6,malloc没有这个功能,这里涉及到调用了new对象的构造函数进行的初始化。

返回类型:

new返回的类型是对象类型的指针,与对象强匹配,而malloc返回的类型是void* ,需要在返回后加类型强转,所以new是更安全的操作方式

自定义类型:

new的调用顺序是:new->operator new函数申请足够的内存-> new的构造函数初始化成员变量->返回自定义类型的指针,  delete->调用析构函数->调用operator delete函数删除内存空间

内存泄漏:

内存泄漏对于new和malloc都能检测出来,new可以明确指出是哪个文件的哪一行,但是malloc不可以明确指出是哪个文件的哪一行。

3.static和const以及extern用法

static:

        在C语言中有三大作用:

        1》 修饰全局变量

                通过static修饰的全局变量,又称静态全局变量,是和全局变量一样存放在静态存储区,在程序执行前就已经初始化了,在程序执行完才会释放

        2》 修饰函数中局部变量

                static修饰局部变量,则该变量就具有全局的属性,在函数调用时被创建生效,在程序结束后释放

        3》修饰函数

                static修饰的函数,只可以在本文件中被调用,不可以在别的文件通过include调用,具有一定的隐私性

        在C++中有两大作用:
        1》修饰类中成员变量

        被static修饰的成员称之为静态成员,静态成员变量是所有的对象共享的,且静态成员变量并不包含在对象中(不占据对象的内存空间),它是类的一个属性,并不是单独一个对象的属性。

        2》修饰类中成员函数

        static修饰的成员函数称之为静态成员函数,他和普通成员函数的区别是既可以通过对象.的方式调用,也可以通过域解析操作符直接新型访问(前提是该静态成员函数必须是public)如:

class Myclass()
{
    public:
         static int x;
    static void setx(int val) {x = val;}
    static int getX(){return x;}
};
int Myclass::x = 0;

int main(){
    Myclass::setX(10);  //可以直接通过类名调用
    std::cout<<Myclass::getX()<<std::endl;
    return 0;
}

,即双重保险(public:static)

const:

        1》const修饰变量,在初始化后就不可以更改。

        2》const用于修饰函数的参数,用于防止传入的参数在函数内被改变,防止错误操作

        3》const修饰函数的返回值,保证函数的返回值是个只读值,不会被更改

        4》修饰指针,保证指针的指向和指针所指的值不可改变;如const int * p = &num(常量指针),则*p=66,这个操作会出错,值不可以更改,但是却可以改变指针的指向,如p = &num1; 

若int const *p = &num (指针常量),则*p = 66是允许的,而p = &num1是不被允许的;

const: 左定值,右定向 const修饰不变量

extern:

        1> 声明是告诉编译器变量的名称和类型,不分配内存,不初始化赋值

        定义是给变量分配内存,可以赋初值

        2> extern可以在A文件中声明B文件中的变量或者函数,这样就可以在A文件中直接引用B文件中的变量,相对于#include B 省去引用多个无用不相关的内容,会加速编译的过程

        3> 函数的声明 extern其实是可以不加的,默认是就有声明的作用

4.指针和引用的区别

指针就是内存的地址,若是32位的操作系统,则指针的大小是4字节。

引用是变量的一个别名,编译器不给引用开辟内存空间。他和引用的变量共用同一块内存

引用的用途主要有:修饰函数的形参和返回值

        在C++语言中,函数的参数和返回值的传递方式有三种:值传递,指针传递和引用传递.引用具有指针的效率,又具有变量使用的方便性和直观性.

实际上引用可以做的事,指针都可以做,为什么还要引用呢?

引用体现了最小特权原则,即给予程序元素完成其功能的最小权限. 指针能够毫无约束的操作内存中的任何东西,尽管功能强大,但是非常危险.

区别是:

        1》引用在初始化后不可以更改,而指针可以更改指向对象

        2》引用在定义时必须初始化,而指针不需要

        3》指针有nullptr,引用则没有空引用

        4》sizeof()执行的含义不同,引用sizeof()则是引用对象的内存大小,而指针的sizeof()则是地址的大小,一般地址32位的就是4字节

        5》引用比指针相对起来更加安全

        6》有多级指针,但是没有多级引用

5.指针函数和函数指针

指针函数,简单理解就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。声明格式如下:

*类型标识符 函数名(参数表)

例如,

int *myfun(int x,int y);

函数指针,其本质是一个指针变量,该指针指向这个函数。函数指针就是指向函数的指针。声明格式如下:

类型说明符 (*函数名) (参数)

例如,

int (*myfun)(int x,int y);
myfun = Function;等价与 Function(int x,int y)

6.指针数组和数组指针

指针数组是指数组元素是由指针组成的,如:

int * array[] = [int* p1,int*p2....]

数组指针则是指向数组的地址,如:

int (*p)[] = [int a1,int a2,int a3...],p则指向该整形数组的首地址

7.内存对齐

 根据计算机地址总线位数来确定,例如:

struct{

    char a;
    int b;
}stu;
print(sizeof(stu))  //8字节,原因是32地址总线为了内存获取速度,常常以32为以此寻址的步长,因此放在
步长的开始则有理由加速寻址

CPU对内存访问速度是偏慢的,因此减少访问的次数是有效加速的手段

8. 封装、继承、多态

1.封装
(1)封装是实现面向对象的第一步,封装就是将数据或函数等集合在一个单元中(类)。被封装的对象通常被称为抽象数据类型。类就具有封装性,通过将数据和方法组合在一起,具有很好的整体性,并且通过将类中的内部数据隐藏保护起来,使得外部函数只能通过类中的公共成员函数才能访问类的内部数据,保护类中的成员不会被无意中破坏

(2)封装的一般原则:高内聚、低耦合,及尽量减少类之间的关联程度,面向对象的设计

public:

通常使用public设置公有成员,让本类之外的其他函数能够通过公有成员,按照类允许的方法访问类的私有数据,就能起到数据保护的目的。

private:

通常使用private把不想让其他程序访问的数据或者函数设置成私有成员,可禁止其他程序对这些数据随意修改。

protected:

通过protected设置保护成员,在封装中没什么用,通常在继承中使用。

2.继承
继承主要实现重用代码,节省开发时间

1)继承是在已有类的基础上创建新类的过程,已有类称为基类(父类),新类称为派生类(子类);

2)派生类继承了基类的功能,且能够对基类的功能进行扩充、修改或重定义。

3)派生类复用了基类的全体数据和成员函数,具有从基类复制而来的数据成员和成员函数(基类私有成员可被继承,但是无法被访问)

4)派生类可以从一个或多个基类派生;通过一个基类派生的继承称为单继承,多个基类派生的继承为多重继承

5)一个类可以作为多个类的基类,一个派生类也可以作为另一个类的基类。

6)继承可传递 如果C从B中派生,B又从A中派生,那么C不仅继承了B中声明的成员,同样也继承了A中的成员。Object类作为所有类的基类。

7)基类中的构造函数、析构函数、友元函数、静态数据成员、静态成员函数都不能被继承。基类中成员的访问方式只能决定派生类能否访问它们。

8)基类的程序代码可以被派生类服用,提高了软件复用的效率,缩短了软件开发的周期

9)派生类如果定义了与继承而来的成员同名的新成员,就可以覆盖已继承的成员。但这并不因为这派生类删除了这些成员,只是不能再访问这些成员。

继承方式:

语法:

class 派生类名:[继承方式]  基类名{
        派生类成员声明与定义
};

公有继承(public):基类成员的访问权限在派生类中保持不变

私有继承(private):基类的private成员在派生类仍是private成员,但是基类的public和protected成员在派生类中会变成private成员。

保护继承(protected):

a)具有protected权限的数据,在该类没有被继承的情况下,protected访问属性与private完全相同。

b)在继承结构中,基类的protected成员不能被派生类的外部函数访问,但是可被派生类直接访问

多态
多态:  指不同对象接收到同一消息时会产生不同的行为(一个接口,多种方法)

简单来说,就是在同一个类或继承体系结构的基类与派生类中,用同名函数来实现各种不同的功能

多态的三个条件:

a) 继承的存在(继承是多态的基础,没有继承就没有多态).
b) 子类重写父类的方法(多态下调用子类重写的方法).
c) 父类指针指向子类对象(子类到父类的类型转换).

多态性主要体现在:向不同的对象发送同一个消息,不同对象接收到消息时产生不同的行为,即每个对象以自己的方式响应同样的消息。

        

        

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值