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) 父类指针指向子类对象(子类到父类的类型转换).
多态性主要体现在:向不同的对象发送同一个消息,不同对象接收到消息时产生不同的行为,即每个对象以自己的方式响应同样的消息。