0%

C++ 知识点总结

头文件保护

目的:多个源文件中可能包含同一个头文件,头文件被重复包含,导致重定义错误

方法一:Include Guard

1
2
3
4
5
6
7
8
9
#ifndef MY_CLASS_H    // 如果 MY_CLASS_H 没有被定义
#define MY_CLASS_H // 定义 MY_CLASS_H

// 头文件内容
class MyClass {
int a;
};

#endif // MY_CLASS_H

方法二:#pragma once

1
2
3
4
5
6
7
#pragma once

class MyClass {
public:
MyClass();
void doSomething();
};

Include Guard 的命名规则:

  1. 使用头文件的名词
  2. 添加项目或者模块前缀
1
2
#ifndef PROJECTNAME_MY_CLASS_H
#define PROJECTNAME_MY_CLASS_H

C++的模板和Java的泛型对比

C++ 的模板和 Java 的泛型虽然都用于支持泛型编程,但在具体实现和功能上有显著区别。以下是它们的对比:


1. 编译时与运行时的区别

  • C++ 模板

    • 编译时处理。
    • 编译器为每个实例化的类型生成一个独立的代码版本(模板实例化),所以是 静态多态
    • 这可以在编译时优化代码,但也会增加编译时间和代码体积(代码膨胀)。
  • Java 泛型

    • 运行时通过类型擦除实现。
    • 类型参数在编译后被擦除,泛型类型被替换为原始类型(如 Object)。
    • 所以在运行时泛型类型的信息不可用。

2. 类型安全

  • C++ 模板

    • 类型是严格检查的。如果传递的类型不满足模板要求,会在编译时报错。
    • 模板代码必须支持所传入的类型(比如,模板中调用了传入类型的某些方法或操作符)。
  • Java 泛型

    • 通过类型擦除的机制,运行时只会检查类型为擦除后的原始类型(如 Object)。
    • 编译器在编译时会对泛型进行类型检查,防止运行时类型转换错误。
    • 使用 <? extends T><? super T> 可以灵活地限制类型范围。

3. 类型擦除

  • C++ 模板

    • 没有类型擦除,模板实例会保留具体类型的信息。
    • 不同类型的模板实例会生成不同的机器代码。
  • Java 泛型

    • 使用类型擦除,编译器会将泛型替换为原始类型(如 Object),并插入必要的类型转换。
    • 运行时没有泛型类型信息,因此不能使用反射操作泛型参数类型。

4. 功能差异

  • C++ 模板

    • 支持 非类型模板参数
      1
      2
      3
      4
      template <typename T, int N>
      class Array {
      T data[N];
      };
      • 模板参数可以是值(如 int)、类型(如 typename)、甚至模板(模板模板参数)。
    • 可以用于函数、类、别名等多种用途。
    • 支持模板特化和部分特化:
      • 完全特化
        1
        2
        template <>
        class MyClass<int> { /* 特化实现 */ };
      • 部分特化
        1
        2
        template <typename T>
        class MyClass<T*> { /* 针对指针类型的实现 */ };
    • 可以结合模板元编程,进行复杂的编译时计算。
  • Java 泛型

    • 主要用于类和方法的类型参数化。
    • 不支持非类型参数(如 int)。
    • 没有模板特化的概念。
    • 不能在泛型中使用基本类型(如 intdouble),需要使用对应的包装类(如 IntegerDouble)。

5. 泛型编程的灵活性

  • C++ 模板

    • 功能更强大,几乎可以用来实现任何形式的编译时泛型。
    • 结合模板元编程,可以实现复杂的逻辑,比如条件判断、递归计算等。
  • Java 泛型

    • 更简单,主要用于确保类型安全的集合和方法。
    • 类型系统不如 C++ 模板灵活,但更容易使用和维护。

6. 示例对比

C++ 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;

template <typename T>
T add(T a, T b) {
return a + b;
}

int main() {
cout << add<int>(1, 2) << endl; // 输出 3
cout << add<double>(1.5, 2.5) << endl; // 输出 4.0
return 0;
}

Java 泛型

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static <T> T add(T a, T b) {
// 不能直接操作 a 和 b 的值
return a; // 示例仅返回 a
}

public static void main(String[] args) {
System.out.println(add(1, 2)); // 自动装箱为 Integer
System.out.println(add(1.5, 2.5)); // 编译错误:不能直接操作 double
}
}

7. 编译与运行效率

  • C++ 模板
    • 在编译时生成独立代码,效率高,但编译时间长,代码体积可能较大。
  • Java 泛型
    • 通过类型擦除减少运行时开销,但在运行时可能需要额外的类型检查和转换。

总结

特性 C++ 模板 Java 泛型
处理时间 编译时处理,生成具体类型的代码。 编译时检查类型,运行时使用类型擦除。
类型信息 类型信息在编译和运行时都保留。 运行时类型信息被擦除,只保留原始类型。
类型安全 编译时检查,类型要求更严格。 编译时检查,运行时依赖类型擦除和转换。
灵活性 支持非类型参数、模板特化、模板元编程。 主要用于类和方法的类型参数化,不支持特化。
性能 高效,但可能导致代码膨胀。 更小的代码体积,但运行时可能需要类型转换。
易用性 较复杂,适合高性能和灵活性要求的场景。 简单易用,适合日常开发中的类型安全需求。
  • C++ 模板 更强大灵活,适合高性能系统或需要复杂编译时计算的场景。
  • Java 泛型 更简单易用,适合大多数面向对象开发需求,尤其是类型安全的集合操作。