cpp 编译

使用 g++ 进行编译。

c 唯一的区别就是从 gcc 变成了 g++

gcc 编译 cpp 程序可能会出问题,无法编译成功。

g++ 与 gcc 的区别

编译器主要用途默认行为
gcc编译 C 代码链接 C 标准库
g++编译 C++ 代码链接 C++ 标准库

这两个底层都是GNU 编译器集合的前端,共享大部分代码,就是链接的标准库不同。

gcc 连接 c 标准库。

g++ 连接 cpp 标准库。

class

类就是蓝图。

访问控制,字段与方法

cpp 中的 class 根据访问权限来写数据,其实感觉还挺清爽的,很有区分度:

class Student{
	private:
		std::string name;
		int score
		int id;
	public:
		bool isAlive;
		int getId(){
			return id;
		}
}

构造函数与析构函数

构造函数

用来创建对象的时候,进行初始化。

写法:

class Student{
	private:
		std::string name;
		int age;
	public:
		Student(std::string pName, int pAge){
			//这里的p代表的是parameter参数,用来与字段区分
			name = pName;
			age = pAge;
		}
}

更简单与推荐的写法:

public:
	Student(std::string pName, int pAge): name(pName), age(pAge){}

前者与后者在外部表现虽然一样,但是底层完全不同,后者是初始化,前者是默认初始化完了再去赋值。

所以后者更加高效比起前者,推荐写后者,虽说与 java 不太一样。

cpp 中也有 this 指针,但是同样不推荐使用,性能比起初始化要差。

析构函数

对象销毁的时候自动调用析构函数。

前面加个 ~

class Student{
	public:
		~Student(){
			
		}
}

类内定义与类外定义

类内定义就是写类的时候直接写在 .cpp 中,定义也就是在类的内部直接写了。

这个其实不好,因为如果类多了,类直接的交互也多了,就非常麻烦,你总是要向前定义,比如你创建了一个 Monster 类,需要将 Player 类作为对象,为了程序能够进行不报错,就得把 Player 写在 Monster 前面,但是 Player 类也需要将 Monster 类作为对象,那就没法写了。

更推荐类外定义,也就是将类写在 .h 或者 .hpp 里面,类内也都是一些函数原型,而不是函数定义。

真的定义在 .cpp 文件中,Player::display(){} 进行定义。

令人烦恼的解析

无参数创建一个对象是:

Game game;
// 或者cpp风格
Game game{};
// 而不是Game game() 这是声明一个函数,创建一个game函数,返回Game类型,无参数

vector

vector 数组是一个对象,而不像是 c 语言中的数组一样只是一个指针。

cpp 中的三种参数传递

传引用

void popOne(vector<int> &nums) {  // 引用,修改原对象
    nums.pop_back();
}
 
// 调用
popOne(nums);  // 直接传递

传指针

void popOne(vector<int> *nums) {  // 指针,需要解引用
    if (nums != nullptr) {
        nums->pop_back();
    }
}
 
// 调用  
popOne(&nums);  // 需要取地址

传值(这个是错误的)

void popOne(vector<int> nums) {   // 值传递,创建副本
    nums.pop_back();              // 只修改副本,不影响原对象!
}
 
// 调用
popOne(nums);  // 原nums不会被修改

规则

// 声明时有 & → 调用时不要 &
void func(Type& param);    // 调用: func(variable)
 
// 声明时有 * → 调用时要 &
void func(Type* param);    // 调用: func(&variable)

cpp 中的 lambda 表达式

[](参数){函数体}

为什么使用 Lambda

优点说明
简洁不需要单独定义函数
封装逻辑紧挨着使用的地方
灵活可以捕获外部变量
现代C++11+ 的标准写法

cpp 版本

C++ 标准发布周期:

  • C++98 - 1998年(第一个国际标准)
  • C++03 - 2003年(小修订)
  • C++11 - 2011年(重大更新,现代C++的开始)
  • C++14 - 2014年(小改进)
  • C++17 - 2017年(重要更新)
  • C++20 - 2020年(重大更新)
  • C++23 - 2023年(最新版本)

最多人用的版本

c++17。

这个也是当前的主流。

lazyvim 自带的 clangd 默认最新启用到 c++17,用 c++20 的一些函数与语法会报错。

auto

c++11 引入的一个类型推导关键字,让编译器自动推断变量类型。

类似于 java 中的 var

好处是更简洁,不用写冗长的类型名。

如果容器类型改变,也不需要修改迭代器的类型了。

性能

编译时,auto 会被替换为具体类型,生成的机器码完全一样。

所以性能与直接写类型名没有任何区别,不影响运行性能,可能编译时会慢一丢丢。

cpp 中的继承与多态

继承

class Base {
public:
    virtual void attack() { cout << "Base attack" << endl; }
};
 
class Derived : public Base {  // 公有继承
public:
    void attack() override { cout << "Derived attack" << endl; }
};
  • virtual 声明虚函数
  • override 明确重写

多态

Base* ptr = new Derived();
ptr->attack();  // 输出 "Derived attack" - 多态

通过基类指针/引用调用实现多态

迭代器

迭代器是泛化的指针。

提供了类似指针的接口,但是功能更加强大。

迭代器是指针的抽象。

cpp 中的 false 值

  1. 布尔类型,false
  2. 空指针,nullptr
  3. 整数,0
  4. 空指针常量,NULL,这个是 C 风格的,不推荐
  5. 浮点数,0.0
  6. 空字符,'\0'
  7. 空字符串,这个要注意,因为指针非空也会执行

除此之外的其他值就都是 true 了

cpp 中的 stack

stack.pop() 没有返回值。

这是为了异常安全的考虑。

pop() 返回被移除的元素,需要在返回前进行拷贝,如果拷贝过程中发生异常,元素既被移出栈又无法正确返回,就会导致数据丢失

  • Java: pop() 有返回值
  • Python: pop() 有返回值
  • C#: Pop() 有返回值
  • C++: pop() 无返回值

cpp 中的循环依赖

指的是两个或多个模块互相引用对方,也就是:

// 文件 A.h
#include "B.h"
class A {
    B* b;  // A 依赖 B
};
 
// 文件 B.h  
#include "A.h"
class B {
    A* a;  // B 依赖 A → 循环依赖!
};

会导致编译错误。

解决方法

使用前向声明,只声明一个 class B,来替代头文件。

cpp 中的函数修饰符位置

class Comparison {
public:
    // 函数"类型"修饰符 - 在前面
    // 影像函数的性质
    virtual void func1();     // 虚函数
    static void func2();      // 静态函数
    inline void func3();      // 内联函数
    
    // 函数"行为"修饰符 - 在后面
    // 是函数的行为约束  
    void func4() const;       // 常量成员函数
    void func5() override;    // 重写确认
    void func6() final;       // 禁止重写
    void func7() = 0;         // 纯虚函数
};

虚函数与纯虚函数

特性虚函数纯虚函数
语法virtual void func();virtual void func() = 0;
实现有默认实现没有实现
类性质可以是具体类使类成为抽象类
实例化可以实例化不能实例化
重写要求派生类可选重写派生类必须重写

虚函数

#include <iostream>
 
class Animal {
public:
    virtual void makeSound() {  // 虚函数 - 有默认实现
        std::cout << "Animal makes a sound" << std::endl;
    }
};
 
class Dog : public Animal {
public:
    void makeSound() override {  // 可以选择重写
        std::cout << "Woof! Woof!" << std::endl;
    }
};
 
class Cat : public Animal {
    // 没有重写 makeSound(),使用基类的默认实现
};
 
int main() {
    Animal animal;  // 可以实例化
    Dog dog;
    Cat cat;
    
    animal.makeSound();  // 输出: Animal makes a sound
    dog.makeSound();     // 输出: Woof! Woof!
    cat.makeSound();     // 输出: Animal makes a sound
}

纯虚函数

#include <iostream>
 
class Shape {  // 抽象类
public:
    virtual double area() const = 0;  // 纯虚函数
    virtual void draw() const = 0;    // 纯虚函数
    
    // 可以有非虚函数
    void printInfo() const {
        std::cout << "Area: " << area() << std::endl;
    }
};
 
class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}
    
    // 必须实现所有纯虚函数
    double area() const override {
        return 3.14159 * radius * radius;
    }
    
    void draw() const override {
        std::cout << "Drawing Circle" << std::endl;
    }
};
 
class Rectangle : public Shape {
private:
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    
    double area() const override {
        return width * height;
    }
    
    void draw() const override {
        std::cout << "Drawing Rectangle" << std::endl;
    }
};
 
int main() {
    // Shape shape;  // 错误!不能实例化抽象类
    
    Circle circle(5.0);
    Rectangle rect(4.0, 6.0);
    
    circle.printInfo();  // 输出: Area: 78.5397
    rect.printInfo();    // 输出: Area: 24
    
    Shape* shapes[] = {&circle, &rect};
    for (auto shape : shapes) {
        shape->draw();  // 多态调用
    }
}

h 与 hpp 头文件辨析

.h 更通用,.hpp 明确声明这是一个 cpp 项目。

两者选其一即可,但是不要混用。

调用 cpp 最大值

使用 limits 头文件。

#include <limits>
 
// 整数类型最大值
int max_int = std::numeric_limits<int>::max();
long max_long = std::numeric_limits<long>::max();
 
// 浮点数类型最大值
float max_float = std::numeric_limits<float>::max();
double max_double = std::numeric_limits<double>::max();
 
// 最小值
int min_int = std::numeric_limits<int>::min();

cpp 中 struct 与 class 的区别

主要区别在默认的访问控制默认的继承方式。除此之外,它们在功能上是完全相同的。

默认访问权限

  • struct: 默认成员是 public
  • class: 默认成员是 private

默认继承方式

  • struct: 默认是 public 继承
  • class: 默认是 private 继承

虽说功能相同,但是两者有使用惯例:

  1. struct:用于包含数据的简单数据结构
  2. class: 用于具有复杂行为和封装的对象