C++:一些常见知识点的区别(整理)

struct与class的区别

  • C语言中struct与class的区别:struct只作为一种复杂数据类型定义的结构体,不能用于面向对象编程;C语言没有class关键字。
  • C++语言中struct与class的区别:对于成员访问权限以及继承方式,class默认都是private,struct默认是public;class可以用于表示模板类型,struct不行;一般来说,用到继承时常用class,没用到继承时则使用struct。

内存泄漏与内存溢出的区别

  • 内存溢出(out of memory):是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;例如申请了一个int型的地址空间,但给它存放了long型的数据,就会导致内存溢出。
  • 内存泄漏(memory leak):是指程序申请内存后,无法释放已申请的内存空间,内存泄漏多了会导致可用内存空间变小,进一步造成内存溢出。

堆和栈的区别

  • 管理方式:堆中资源由程序员控制(通过malloc/free、new/delete,容易产生memory leak),栈资源由编译器自动管理。
  • 系统响应:对于堆,系统有一个记录空闲内存地址的链表,当系统收到程序申请时,遍历该链表,寻找第一个大于所申请空间的空间的堆结点,删除空闲结点链表中的该结点,并将该结点空间分配给程序(大多数系统会在这块内存空间首地址记录本次分配的大小,这样delete才能正确释放本内存空间,另外,系统会将多余的部分重新放入空闲链表中)。对于栈,只要栈的剩余空间大于所申请空间,系统就会为程序分配内存,否则报异常出现栈空间溢出错误。
  • 空间大小:堆是不连续的内存区域(因为系统是用链表来存储空闲内存地址的,自然不是连续),堆的大小受限于计算机系统中有效的虚拟内存(32位机器上理论上是4G大小),所以堆的空间比较灵活,比较大。栈是一块连续的内存区域,大小是操作系统预定好的,windows下栈大小是2M(也有是1M,在编译时确定,VC中可设置)。
  • 碎片问题:对于堆,频繁的new/delete会造成大量内存碎片,降低程序效率。对于栈,它是一个先进后出(first-in-last-out)的结构,进出一一对应,不会产生碎片。
  • 生长方向:堆向上,向高地址方向增长;栈向下,向低地址方向增长。
  • 分配方式:堆是动态分配(没有静态分配的堆)。栈有静态分配和动态分配,静态分配由编译器完成(如函数局部变量),动态分配由alloca函数分配,但栈的动态分配资源由编译器自动释放,无需程序员实现。
  • 分配效率:堆由C/C++函数库提供,机制很复杂,因此堆的效率比栈低很多。栈是机器系统提供的数据结构,计算机在底层对栈提供支持,分配专门的寄存器存放栈地址,提供栈操作专门的指令。

指针与引用的区别

  • 指针是一个实体,有分配内存空间,引用只是一个别名,不分配内存空间
  • 指针不用但最好初始化,引用必须初始化
  • 引用只能初始化一次,指针可以多次赋值
  • 可以有const指针,但没有const引用,例如 int* const p合法,int& const p有些编译器会报错,有些编译器会警告并忽视该const修饰符
  • 指针可以指向空值,引用不能指向空值
  • “sizeof引用”得到的是变量对象的大小,“sizeof指针”得到的是指针本身的大小
  • 指针和引用的自增运算符意义不同
  • 指针可以有多级,引用只能是一级

五种内存分配区域

  • C/C++编译的程序所占用内存区域一般分为以下5个部分:
    • 栈区(stack):由编译器自动分配和释放,用来存放函数的参数、局部变量等。其操作方式类似于数据结构中的栈。
    • 堆区(heap):一般由程序员分配和释放(通过malloc/free、new/delete),若程序员没有释放,则程序结束时由操作系统回收。它与数据结构中的堆是两回事,分配方式类似于链表。
    • 全局/静态区:全局变量和静态变量的存储是放在一块的,初始化的全局变量和初始化的静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由操作系统回收。
    • 文字常量区:存放常量值,如常量字符串等,不允许修改,程序结束后由操作系统回收。
    • 程序代码区:存放函数体的二进制代码。
  • 例子如下:
#include <stdlib.h>
#include <string.h>
int a = 0; // 全局初始化区
char* p1;  // 全局未初始化区
int main() {
 int a;            // 栈区
 char s[] = "abc"; // 栈区
 char* p2;         // 栈区
 char* p3 = "123456";    // 123456\0在常量区,p3在栈区
 static int c = 0;      // 全局/静态初始化区
 p1 = (char*) malloc(10);
 p2 = (char*) malloc(20); // 分配得来的10和20字节在堆区
 strcpy(p1, "123456"); // 123456\0放在常量区,编译器可能将它与p3所指向的"123456"优化成一个地方
 return 0;
}

new与malloc的区别

  • 属性
    • new/delete是操作符,是C++关键字,需要编译器支持;malloc/free是库函数,需要头文件支持。
  • 参数
    • 使用new操作符动态分配内存时无需指定内存块大小,编译器会根据类型自行计算;malloc分配内存时需要显式地指出所需内存块大小。
  • 返回类型
    • new操作符内存分配成功时会返回相应对象类型的指针,无需进行强制类型转换,符合类型安全性,分配失败时会抛出bac_alloc异常;malloc分配内存成功时会返回void*类型的指针,需要通过强制类型转换为所需类型,分配失败时返回NULL值。
  • 非内部数据对象
    • new会先调用operator new函数,申请足够的内存,再调用类型的构造函数,初始化成员变量,最后返回自定义类型的指针;delete会先调用析构函数,然后调用operator delete函数释放内存;malloc/free是库函数,只能动态申请内存及释放内存,无法完成构造函数及析构函数的工作。
  • 重载
    • C++允许重载new/delete(实际上是重载operator new 和operator delete),特别的,布局new(placement new)就不需要为对象分配内存,而是使用指定一个地址作为内存起始区域,new在这段内存上完成对象的构造函数调用并初始化该内存段,并返回此内存地址;malloc/free不允许重载。
  • 内存区域
    • new操作符从自由存储区(free stone)上为对象动态分配空间,malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请的,该内存即为自由存储区;堆是操作系统中已分配的对应的内存区域。自由存储区不等于堆,布局new就可以不位于堆中。堆是一个实际的区域,而自由存储区是一个更上层的概念。通常new确实是在堆上申请内存,但是程序员可以自己重载new操作符,使用其他内存来实现自由存储(不常见),另外,C++primer plus这本书上提到的布局new,可以为对象在栈上分配内存。总的来说,自由存储区是new申请的区间的概念。

3种类间关系及区别

Inheritance(继承)

  • 表示is-a,如下代码所示:
class A {
private:
    int a;
};

class B : public A {
private:
    int b;
};
  • 继承关系下的构造和析构
    ==构造由内而外==
    Derived的构造函数首先调用Base的default构造函数,然后才执行自己。如,Derived::Derived(...) : ==Base()== { ... };
    ==析构由外而内==
    Derived的析构函数首先执行自己,然后才调用Base的析构函数。如,Derived::~Derived(...) { ... ==~Base()== };

Composition(复合)

  • 表示has-a,如下代码所示:
class A {
private:
    int a;
};

class B {
private:
    A a;
    int b;
};
  • 复合关系下的构造和析构
    ==构造由内而外==
    B的构造函数首先调用A的default构造函数,然后才执行自己。如,B::B(...) : ==A()== { ... };
    ==析构由外而内==
    B的析构函数首先执行自己,然后才调用A的析构函数。如,B::~B(...) { ... ==~A()== };

Delegation(委托)

  • Composition by reference, 如下代码所示:
class A {
private:
    int a;
};

class B {
private:
    A* a;
    int b;
};

猜你喜欢

转载自www.cnblogs.com/yiluyisha/p/9188010.html