【C++总览】

基础篇

C/C++的区别

面向对象三大特性:封装、继承、多态

封装:

封装是指将数据和操作这些数据的方法捆绑在一起,形成一个类。封装的核心思想是隐藏对象的内部实现细节,只暴露必要的接口给外部使用,保护数据的安全性和完整性。

  • 数据隐藏:通过控制类的成员的访问权限,避免外部直接访问或修改类的内部状态。
  • 接口暴露:只通过公开的方法来访问和修改类的内部数据。

继承:

继承是指子类可以继承父类的属性和方法。子类可以扩展或修改父类的功能,从而实现代码的重用和层次化结构。

  • 父类(基类):包含通用的属性和方法。
  • 子类(派生类):继承父类的属性和方法,增加特有的功能或修改父类方法。

多态:

多态是指同一个方法在不同的对象上有不同的表现。它允许不同的对象通过相同的接口来调用不同的实现。多态通过方法重写和方法重载来实现。

  • 方法重写(运行时多态):子类重新定义父类的方法,实现不同的行为。
  • 方法重载(编译时多态):同一个方法名根据参数的不同实现不同的功能。

好处:

  • 灵活性:允许不同的对象对同一消息做出不同的响应,增强程序的灵活性。
  • 可扩展性:新增子类时,无需修改现有代码,只需实现或重写父类的方法。
  • 简化代码:通过统一的接口处理不同的对象,减少条件判断代码。
  • 解耦合:调用者无需关心具体实现,只需关注接口。

面向过程与面向对象的区别

C语言结构体与C++类的比较

指针与引用的区别

  • 指针是一个变量,存储另一个变量的地址。引用是对一个已经存在的变量起别名,不单独占用内存空间。
  • 指针可以多级,引用只有一级
  • 指针可以不初始化,引用必须初始化
  • 指针指向可变,引用不可变
  • 指针可以为空,引用不能为空
  • 对指针sizeof得到4或8,对引用sizeof得到引用对象所占大小

C++关键字

const

用来修饰函数、变量、对象,const在有些情况下是编译时确定,有些情况下是在运行时确定。

常引用可不可以改? 

一旦常引用被初始化,它所引用的对象的值不能通过该常引用来修改,但可以通过其他途径修改该对象的值(前提是该对象本身不是常量),举例:

#include <iostream>
int main() {
    int num = 10;
    const int& ref = num;
    // ref = 20; // 编译错误,不能通过常引用修改所引用的对象的值
    num = 20; // 可以通过原始变量修改对象的值
    std::cout << ref << std::endl; // 输出 20
    return 0;
}

const与constexpr的区别

const:

  • 用于声明一个变量为只读,即其值在初始化后不能被修改。

  • const 变量的值可以在运行时确定,也可以在编译时确定

constexpr:

  • 用于声明一个变量或函数在编译时就可以被求值,即其值必须在编译期间确定

  • constexpr 不仅保证了变量的不可变性,还确保了其在编译时的可计算性。

constexpr相比于const的优势: 

1. 编译时计算能力

constexpr 允许在编译时进行复杂的计算,从而提高程序的性能。这对于需要高性能的应用尤为重要。

constexpr int factorial(int n) {
    return (n <= 1) ? 1 : (n * factorial(n - 1));
}

constexpr int fact_5 = factorial(5); // 编译时计算,fact_5 = 120

2. 更严格的常量性保证

constexpr 不仅保证了变量的不可变性,还确保了其值在编译时是已知的。这有助于编译器进行更多的优化,并减少运行时的错误。

3. 可用于更多上下文

  • constexpr 变量可以用作数组的大小,因为数组大小必须是编译时常量。

constexpr int size = 10;
int arr[size]; // 数组大小为 10
  • constexpr 变量可以用作模板参数,因为模板参数必须是编译时常量。

template<int N>
struct Array {
    int data[N];
};

constexpr int size = 5;
Array<size> arr; // 使用 constexpr 变量作为模板参数
  • constexpr 变量可以用作枚举值,因为枚举值必须是编译时常量。

constexpr int value = 42;

enum MyEnum {
    VALUE = value // 使用 constexpr 变量作为枚举值
};

static

static 类内

static 修饰成员属性:

1.不依赖对象,类共用的,不可以被继承(继承得到的是拷贝的一份属性,而静态成员只有一份,对象之间共享,所以不能继承),需要在类外初始化。

2.不能在.h文件初始化(重定义的问题),要在.cpp文件初始化。

static 修饰成员函数:

没有this指针,可以不依赖对象使用,类共用的,不可以声明为虚函数,不能直接调用类中的非静态成员,可以利用对象调用。

static 类外

static 修饰类外函数:

只能在当前文件可见,不支持extern。
static 修饰类外变量:

1.只能在当前文件中可见。

2.一旦初始化后,生命周期贯穿整个程序的运行过程,并且只会初始化一次。

static作用、初始化时机、销毁时机:

 修饰类的静态成员变量

  • 作用

    • 属于类本身,而不属于类的某个具体对象,被类的所有对象共享。

    • 可以通过类名直接访问(需要作用域解析运算符::),也可以通过对象访问,但推荐使用类名访问以体现其静态属性。

  • 初始化时机

    • 在类外进行初始化,通常在源文件(.cpp)中进行。初始化时需要指定类名和作用域解析运算符。对于内置类型(如intdouble等),如果没有显式初始化,静态成员变量会被默认初始化为0;对于类类型,默认调用其默认构造函数进行初始化。

  • 销毁时机

    • 静态成员变量的生命周期贯穿整个程序的运行期间,在程序结束时(main函数返回后),随着全局静态存储区的销毁而销毁。

修饰类的静态成员函数

  • 作用

    • 属于类本身,不依赖于类的任何对象。静态成员函数没有this指针,因此不能访问非静态成员变量和非静态成员函数,只能访问静态成员变量和其他静态成员函数。

  • 初始化时机

    • 静态成员函数不需要像静态成员变量那样进行专门的初始化,它们随着类的定义而被创建,在程序开始运行时就已存在。

  • 销毁时机

    • 和静态成员变量一样,在程序结束时销毁。

修饰全局变量

  • 作用

    • 限制全局变量的作用域为定义它的文件内部,其他文件无法通过extern关键字访问该全局变量,从而实现信息隐藏,避免命名冲突。

  • 初始化时机

    • 在程序启动时,也就是main函数执行之前进行初始化。初始化顺序在不同的编译单元(源文件)之间是不确定的,但在同一个编译单元内,按照变量定义的先后顺序进行初始化。

  • 销毁时机

    • 在程序结束时(main函数返回后)进行销毁,销毁顺序与初始化顺序相反。

修饰局部变量

  • 作用

    • 改变局部变量的存储方式,使其存储在静态存储区而不是栈区。局部静态变量只会被初始化一次,在函数调用结束后不会销毁,下次调用该函数时,能保留上一次调用结束时的值。

  • 初始化时机

    • 在第一次执行到该局部静态变量的定义语句时进行初始化,且只初始化一次。初始化完成后,后续再调用包含该变量的函数时,不会再对其进行初始化操作。

  • 销毁时机

    • 在程序结束时(main函数返回后)进行销毁 。

virtual

修饰虚函数,实现动态多态。父类是虚函数时,子类同名的成员函数默认是虚函数。

哪些函数不能被virtual修饰?

构造 static inline friend 类外的普通函数

不能修饰构造函数:虚函数机制依赖于对象的完全类型信息,而在构造函数阶段,基类部分的对象已被创建,但派生类的部分尚未构建。此时,如果构造函数是 virtual,它可能无法正确调用派生类的重写函数。(可参考多态实现过程,首先执行父子构造,初始化对象,然后进行运行时调用,如果构造声明virtual,那么相当于父类对象构建完成而子类对象没构建完成就进行多态,可能会导致无法正确调用派生类的重写函数)。

不能修饰static:1.static 修饰的类成员函数无 this 指针:虚函数的调用需要通过 this 指针来访问该对象,this 指针指向调用虚函数的实际对象。2.static 成员函数在编译时绑定,不具备运行时多态性,而 virtual 关键字用于实现运行时多态。

不能修饰inline:inline在编译时在执行的位置直接展开函数,而多态是在运行时确定,二者矛盾。

不能修饰friend:​1.不属于类的成员:friend 函数不是类的成员函数,它们只是被授予访问类的私有和保护成员的权限。虚函数的机制仅适用于类的成员函数,因为它们依赖于对象的虚函数表,而 friend 函数没有与之关联的对象实例。2.缺乏 this 指针:类似于 static 函数,friend 函数也不属于类的实例,没有 this 指针,因此无法支持虚函数的动态绑定。

不能修饰类外的普通函数:类外的普通函数完全独立于类,没有与之关联的对象实例,因此没有 this 指针,无法支持虚函数的动态绑定。

inline

注:构造、析构函数可以是内联的,虚析构不能是内联。

friend

纯虚函数

operator重载操作符

自定义实现赋值重载操作符(如果不手动实现,编译期默认提供operator=函数,是浅拷贝)

1.浅拷贝

class A
{
private:
	int val;
public:
	A& operator = (const A& a)
	{
		//判断异常,查看a是不是当前的对象
		if (this == &a)return *this;
		//浅拷贝情况
		this->val = a.val;
		return *this;
	}
};
int main() {
	A a1;
	A a2;
	a1 = a2;
	a1 = a1;
}

2.对于含有指针成员的情况,考虑深拷贝

class A
{
private:
    int* val;
public:
    A()
    {
        val = new int;
    }
	A& operator = (const A& a)
	{
		//判断异常,查看a是不是当前的对象
		if (this == &a)return *this;
        //释放当前对象的旧内存(如果有)
        delete val;
        //为当前对象分配新的内存
        val = new int(*a.val);
        return *this;
	}
    ~A()
    {
        delete val;
    }
};

访问权限

访问权限

构造函数

在手写智能指针时用到了浅拷贝。 

手写智能指针

拷贝构造

初始化列表

虚指针的初始化也是在初始化列表中完成。 

析构函数

类与类

类之间的关系

在我之前的文章:类之间的横向关系

多态

静态多态

是编译时确定的(具体执行什么函数),可以通过函数重载、函数模板实现。

动态多态

通过继承和虚函数实现。只有程序运行的时候,才能确定具体执行什么函数。

动态多态优缺点

优点:代码不变,功能可变,具有良好的拓展和复用性

缺点:

1.空间消耗,运行效率会相对低一些,破坏封装

2.虚函数多态没有办法调用子类特有的虚函数

重载、重写、隐藏

重载:相同作用域下,函数名相同,参数列表不同(个数或类型),返回值可同可不同。

重写(覆盖):指子类实现了和父类虚函数相同的函数,包括函数名,参数列表以及返回值。

隐藏:隐藏是指在派生类中定义了一个与基类中非虚函数同名的函数(返回值、参数列表不做要求),从而隐藏了基类中的同名函数。这里的“隐藏”并不是覆盖基类的函数,而是使得基类的同名函数在派生类中不可见。

子类中包含父类中的同名函数,用子类对象调用时,会调用子类中的同名函数。如果想调用父类的同名函数,需要加父类作用域。无法通过父类指针指向子类对象调用子类的同名函数。

特点:

1.基类函数不是虚函数。

2.函数签名可以相同或不同:即使参数列表不同,基类的同名函数也会被隐藏。

3.静态绑定:调用哪个函数在编译时根据对象的类型决定。

虚指针与虚表

虚函数表的存储位置

虚函数表通常存储在程序的 只读数据段(.rodata 段) 中。​​​​同一类的所有对象共享同一个虚函数表,虚函数表是静态数据,编译时生成,运行时不可修改。

在 Linux 中,.rodata 段通常位于 只读数据区,属于进程的 代码段 的一部分。代码段包括:

可执行指令(.text 段)、只读数据(.rodata 段)。

在 Windows 中通常称为 .rdata 而不是 .rodata,属于进程的 代码段 的一部分。代码段包括:可执行指令(.text 段)、只读数据(.rdata 段)。

C++11

在我之前的文章:C++11知识点

设计原则

在我之前的文章:设计原则

设计模式

在我之前的文章:设计模式

STL

在我之前的文章:C++之STL

多继承

在我之前的文章:C++之多继承

内部类

在我之前的文章:C++之内部类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值