2111 字
11 分钟
C++基础知识1208
2023-12-08

C++中指针和引用的区别#

  • 引用的特性:创建必须初始化,不能初始化为空,不能改变引用的指向,没有常引用
  • 指针可以初始化为空,引用不可以
  • 指针可以指向另一个内存地址,引用不可以改变指向

拷贝构造#

  • 拷贝构造函数的概念:

    • 将一个对象的值复制给另一个对象
      • 参数使用万能引用在C++98中提出,既可以接收右值也可以接收左值
  • 浅拷贝:如果对象有指针成员变量,只会复制指针的地址,而不会复制指针指向的内容

    • 这意味着,原始对象和拷贝对象将共享相同的内存资源,一个对象修改或释放了这块内存,都会影响到其他对象
    • 写法:
    class Person {
    public:
    char* name;
    // 浅拷贝
    Person(const Person& other) {
    name = other.name; // 只复制指针地址
    }
    };
  • 深拷贝:会为对象的指针成员变量分配新的地址空间,并将指针指向的内容也一并复制

    • 这样,原始对象原始对象和拷贝对象将有独立的地址空间,彼此之间的修改互不影响
    • 写法:
    class Person {
    public:
    char* name;
    // 深拷贝
    Person(const Person& other) {
    name = new char[strlen(other.name) + 1]; // 先开辟相同大小的空间
    strcpy(name, other.name); // 复制指针所指向的内容
    }
    };
  • 完整代码:

#include <iostream>
#include <cstring>
class Person {
public:
char* name;
// 构造函数
Person(const char* n) {
name = new char[strlen(n) + 1];
strcpy(name, n);
}
// 拷贝构造函数(浅拷贝)
Person(const Person& other) {
name = other.name; // 浅拷贝,只复制指针地址
}
// 深拷贝构造函数
Person(const Person& other) {
name = new char[strlen(other.name) + 1];
strcpy(name, other.name); // 深拷贝,复制指针所指向的内容
}
// 析构函数
~Person() {
delete[] name;
}
};
int main() {
Person p1("Alice");
Person p2 = p1; // 浅拷贝,p2.name 和 p1.name 指向相同的内存
p1.name[0] = 'B'; // 修改 p1 的 name
std::cout << "p2.name: " << p2.name << std::endl; // 输出 "Blice",p2.name 也被修改了
Person p3(p1); // 深拷贝,p3.name 和 p1.name 拥有独立的内存空间
p1.name[0] = 'C'; // 修改 p1 的 name
std::cout << "p3.name: " << p3.name << std::endl; // 输出 "Blice",p3.name 不受影响
return 0;
}

移动构造#

  • 概念:C++11引入的特性,用于将一个对象的资源==高效的==转移给另一个对象(如动态分配的内存、文件句柄等),而不进行不必要的数据拷贝

    • 作用:可以提高性能
      • 使用场景:
        • 处理大型对象
        • 频繁传递临时对象
    • 如何接收被移动的对象:使用右值引用,并将其资源转移到新对象中,同时原对象会被置为==有效==但==未指定状态==,避免资源==重复==释放
    • 移动构造通常使用std::move函数将对象转换为右值引用
  • 写法:

class MyString {
public:
char* data;
int length;
// 移动构造函数
MyString(MyString&& other) noexcept : data(other.data), length(other.length) {
other.data = nullptr; // 将原始对象的资源置为空指针
other.length = 0;
}
};
  • noexcept:
    • 写法:写在函数末尾void fun() noexcept {}
    • 作用:表示该函数不会抛出异常

静态变量#

  • 分为:静态全局变量和静态局部变量

  • 算上静态成员变量,三者的区别:

    1. 作用域:

      • 静态全局变量:
        • 整个源文件
          • 在定义它的源文件中,源文件的任何位置都能访问
          • 注意:在头文件中创建全局变量,要使用静态全局变量
      • 静态局部变量:
        • 定义它的函数内
          • 函数外无法直接访问
      • 静态成员变量:
        • 类内
          • 可以通过类名或对象访问,但是不能通过对象访问非静态成员变量
    2. 存储位置:

      • 静态全局变量:
        • 根据是否被显式初始化放在不同位置:
          • 被显式初始化:data段
          • 没被显式初始化:bss段
            • 没被显示初始化的会被赋值成0或空指针
      • 静态局部变量:
        • 存储在data段
      • 静态成员变量:
        • 存储在静态数据区
    3. 生命周期:

      • 静态全局变量:
        • 与程序生命周期相同
      • 静态局部变量:
        • 从定义位置到程序结束
      • 静态成员变量:
        • 与程序生命周期相同
  • 静态成员变量的特性:

    • 初始化:必须初始化,并且要在类外初始化
    • 属于:属于类不属于对象,存在静态数据区(全局区)
      • 使用sizeof计算的时候不会计算静态成员变量的大小
    • 继承:静态成员变量不会被继承,父类和子类共享静态成员变量,可以通过==类名==或==对象名==访问公有的静态成员变量

初始化参数列表#

  • 特性:
    • 是初始化不是赋值
      • 所以,变量的初始化顺序与成员变量的生命顺序一样,与参数列表中的顺序无关
    • 常量和引用要在初始化参数列表中初始化
      • 显示调用父类构造,因为父类没有无参构造,创建子类对象必须创建父类对象,所以需要在子类的初始化参数列表中显示调用父类的构造函数
        • 构造函数:用于给成员变量赋值,初始化对象

C++初始化顺序,主函数前面执行什么?#

  • 按全局变量的创建顺序初始化全局变量

C和C++的区别#

  1. 重载:

    • C++支持,C不支持
      • 重载的特性:
        • 函数名相同
        • 参数不同(类型,个数,顺序)
        • 与返回值无关
        • 一般发生在同一个==类==或==作用域==中
      • 为什么C不支持:与这两门语言的编译特性有关
  2. 引用:

    • C++支持,C不支持
  3. 多态:

    • C++支持:
      • 父类指针指向子类对象,实现动态绑定
    • C不支持:
      • 多态是面向对象的特性,C是面向过程的语言
  4. malloc和new:

    • C++:

      • 使用new运算符动态分配内存,并自动调用对象的构造函数进行初始化
      • 使用delete运算符释放内存,并自动调用析构函数
    • C:

      • 使用malloc函数分配内存,不会自动调用构造函数初始化
      • 使用free函数释放内存,不会自动调用析构函数
  5. 内存管理:

    • C++:
      • 自动管理内存,避免内存泄漏或者资源的错误使用
        • 创建对象或销毁对象时会自动的调用构造函数和析构函数
    • C:
      • 手动管理内存:
        • 没有自动构造和析构的功能

extern#

  • 用途:用于声明外部变量或函数
    • 注意:对于全局变量,即使不使用 extern 关键字进行声明,如果直接在头文件中定义,也会被默认视为 extern 类型,因此在头文件中定义全局变量时通常不需要加上 extern 关键字

c++类和结构体有什么区别#

  • 默认访问权限和默认继承权限不同
  • 类默认私有,结构体默认公有

野指针与悬挂指针#

  • 野指针:
    • 创建没有初始化
    • 访问时:由于指向的内存区域是未知的或已经被释放的,会导致==程序崩溃==、==数据损坏==等不可预知的后果
  • 悬挂指针:
    • 释放没有置空
    • 访问时:由于指针指向的内存区域已经被释放,会导致==程序崩溃==、==数据损坏==等不可预知的后果

new和malloc的区别#

  • malloc返回值需要强转,new不需要
  • malloc申请失败返回空指针(NULL),new抛出异常
  • new是运算符,malloc是库函数
  • new不需要传递具体的字节数,malloc需要传入字节个数
  • new先调用malloc,再调用构造函数

malloc出错会发生什么#

  • 内存泄漏:如果 malloc 返回空指针,但是程序没有进行处理或检查,可能导致内存泄漏

数组指针和指针数组的区别#

  • 指针数组:存放指针的数组
    • int* a[3];
  • 数组指针:数组的指针,a1+=1,移动的内存是 3 * 4 = 12
    • int (*a1)[3];
      • *和a1被括起来表示的是,a1是指针变量,指向的是大小为12个字节的int类型的数组

指针函数和函数指针#

  • 指针函数:函数的返回值是指针类型
  • 函数指针:指向函数的指针
C++基础知识1208
https://fuwari.cbba.top/posts/c基础知识1208/
作者
Chen_Feng
发布于
2023-12-08
许可协议
CC BY-NC-SA 4.0