1.深拷贝与浅拷贝

  • 浅拷贝

浅拷贝是对象复制时,仅复制对象中的基本数据类型成员变量的值。对于指针类型成员变量,它只是复制指针所存储的内存地址,而非指针指向的实际内存空间内容。这意味着新对象和原对象的指针成员指向同一块内存区域。

  • 深拷贝

深拷贝不仅复制对象的基本数据类型成员变量的值,对于指针类型成员变量,会为其分配全新的内存空间,并将原对象指针指向的内存内容完整复制到新分配的空间中。这样新对象和原对象的指针成员指向不同的内存区域,数据相互独立。

2.引用和指针的区别

  • 指针是一个实体,需要分配内存空间。引用只是变量的别名,不需要分配内存空间。
  • 引用在定义的时候必须进行初始化,并且不能够改变。指针在定义的时候不一定要初始化,并且指向的空间可变。(注:不能有引用的值不能为NULL)
  • 有多级指针,但是没有多级引用,只能有一级引用。
  • 指针和引用的自增运算结果不一样。(指针是指向下一个空间,引用时引用的变量值加1)
  • sizeof引用得到的是所指向的变量(对象)的大小,而sizeof 指针得到的是指针本身的大小。
  • 引用访问一个变量是直接访问,而指针访问一个变量是间接访问。使用指针前最好做类型检查,防止野指针的出现;
  • 引用底层是通过指针实现的;
  • 作为参数时也不同,传指针的实质是传值,传递的值是指针的地址;传引用的实质是传地址,传递的是变量的地址。

3.数组和指针的区别

1. 本质不同

  • 数组:是连续存储的同类型元素集合,核心是 “数据本身”。定义时需明确元素类型和长度(或通过初始化推断),例如 int arr[5]; 表示一块固定大小的连续内存,直接存放 5 个 int 类型数据。
  • 指针:是存储地址的变量,核心是 “指向数据的地址”。定义时只需指定指向的数据类型,例如 int* p; 表示 p 中存放的是一个 int 类型变量的内存地址,本身不直接存储数据。

2. 内存与大小特性不同

  • 数组:内存是定义时一次性分配的固定空间(栈或全局区),大小由 “元素数量 × 单个元素大小” 决定。
  • 指针:指针变量本身仅占 “一个地址的大小”(64 位系统通常 8 字节),与指向的数据无关;其指向的内存可动态分配(如堆上的 new int[5]),大小可灵活调整。

3. sizeof 运算结果不同

  • 对数组用 sizeof,得到整个数组的总字节数。
  • 对指针用 sizeof,得到指针变量本身的字节数。

4. 可修改性不同

  • 数组名是 “常量指针”(指向数组首元素的固定地址),不能被修改。
  • 指针是变量,存储的地址可修改(例如 p++ 会让指针指向当前元素的下一个位置)。

5. 赋值与初始化不同

  • 数组不能整体赋值给另一个数组。
  • 指针可直接赋值。

6. 函数传参时的表现不同

  • 数组作为参数传递时,会隐式退化为指针(丢失长度信息)。
  • 指针传参本质是 “地址的拷贝”,函数内修改指针指向不影响外部,但修改指向的内容会影响外部(与数组传参效果一致,因数组传参本质是传首地址)。

4.C++中值传递和引用传递的区别

  • 指针参数传递本质上是值传递,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,会在栈中开辟内存空间以存放由主调函数传递进来的实参值,从而形成了实参的一个副本(替身)。被调函数对形式参数的任何操作都是作为局部变量进行的,不会影响主调函数的实参变量的值(形参指针变了,实参指针不会变)。
  • 引用参数传递过程中,被调函数的形式参数也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参(本体)的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量(根据别名找到主调函数中的本体)。因此,被调函数对形参的任何操作都会影响主调函数中的实参变量。

5.静态变量什么时候初始化

1. 全局或文件里的静态变量(包括静态全局变量)和类里的静态成员变量

这类变量不属于任何函数或类,作用范围是整个文件或全局。它们的初始化在程序真正开始运行前(main 函数执行之前)。

2. 函数内部的静态变量(局部静态变量)

定义在函数里的static变量,初始化时机是第一次调用这个函数、执行到变量定义这一行的时候。

6.const关键字

  • 阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
  • 对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
  • 在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
  • 对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量,类的常对象只能访问类的常成员函数;
  • const成员函数可以访问非const对象的非const数据成员、const数据成员,也可以访问const对象内的所有数据成员;
  • 非const成员函数可以访问非const对象的非const数据成员、const数据成员,但不可以访问const对象的任意数据成员;
  • 一个没有明确声明为const的成员函数被看作是将要修改对象中数据成员的函数,而且编译器不允许它为一个const对象所调用。因此const对象只能调用const成员函数。
  • const类型变量可以通过类型转换符const_cast将const类型转换为非const类型;
  • const类型变量必须定义的时候进行初始化,因此也导致如果类的成员变量有const类型的变量,那么该变量必须在类的初始化列表中进行初始化;

7.static的用法与作用

1. 修饰局部变量(函数内的变量)

  • 核心作用:
    • 延长生命周期:普通局部变量在函数结束后销毁,而static修饰的局部变量会一直存在到程序结束(存在于全局数据区)。初始化只执行一次,后续调用函数时,变量会保留上次的值(比如累加计数场景)。
    • 作用域不变:仍只能在当前函数内使用,外部无法访问。

2. 修饰全局变量或函数(文件级的变量 / 函数)

  • 核心作用:
    • 限制作用范围:普通全局变量 / 函数可被其他文件访问,static修饰后,只能在当前文件内使用,其他文件无法引用。
    • 避免命名冲突:多文件开发时,不同文件可定义同名的静态全局变量 / 函数,互不干扰。
    • 隐藏实现细节:将模块内部的辅助变量 / 函数设为static,仅暴露必要接口,增强代码封装性。

3. 修饰类的成员(变量或函数,C++ 特有)

  • 核心作用:
    • 实现类级共享:静态成员属于 “类” 而非 “对象”,所有类的实例共用同一份变量 / 函数,不随对象创建 / 销毁而变化。
    • 无需对象即可访问:静态成员函数可直接通过 “类名::函数名” 调用(不用创建对象),适合实现类级工具逻辑(比如统计实例总数)。
    • 访问限制:静态成员函数只能操作静态成员变量,无法直接访问类的非静态成员(因非静态成员依赖具体对象)。

8.extern的用法

1. 跨文件声明全局变量

  • 用法:在当前文件中用extern标注其他文件已定义的全局变量。
  • 作用:告知编译器 “该变量已在其他文件中定义,这里仅引用,无需重新分配内存”,实现多个文件共享同一份全局变量,避免 “未定义” 或 “重复定义” 的错误。

2. 跨文件声明外部函数

  • 用法:用extern声明在其他文件中定义的函数。
  • 作用:函数默认隐含extern属性,显式使用更清晰 —— 明确告知编译器 “函数实现在其他文件中”,确保编译时识别函数、链接时找到正确地址,同时让代码更易读懂(区分 “本文件定义” 和 “外部定义” 的函数)。(例如:在.h中进行函数声明,在.cpp中进行函数实现,就隐式地用到了extern关键字)。

3. C++ 中实现跨语言(C/C++)链接(extern "C")

  • 用法:在 C++ 代码中,用extern "C"包裹 C 语言函数的声明(比如包裹 C 编译的库函数声明)。
  • 作用:解决 C 和 C++ 的 “命名修饰差异”——C++ 会给函数名加参数类型等修饰,C 不会。extern "C"让 C++ 编译器按 C 语言规则处理包裹的函数,确保 C 编译的库能被 C++ 代码正确调用。

9.class和struct的区别

1. 默认成员访问权限不同

  • struct:访问权限是public(公共的)。
  • class:访问权限是private(私有的)。

2. 默认继承方式不同

当作为基类被继承时:

  • struct:默认的继承方式是public 继承(派生类会继承基类的 public 成员为 public,protected 成员为 protected)。
  • class:默认的继承方式是private 继承(派生类会将基类的 public 和 protected 成员都变为 private,外部无法访问)。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐