C++11
decltype类型推导
decltype不依赖于初始化,根据表达式类推导类型
- auto b :根据右边a的初始值来推导出变量的类型,然后将该初始值赋给b
- decltype 则是根据a表达式来推导类型,变量的初始值与表达式的值无关
- 表达式类型注意点:如果表达式是左值,则推导出的类型也是左值引用
int a = 10;
auto b = a;
decltype(a) b = a; // b的类型是int
int add(int a, int b) {
return a + b;
}
decltype(add(1, 2)) result; // result的类型是int
int x = 5;
int& ref = x;
decltype(ref) newRef = x; // newRef的类型是int&
int func(int a, int b) {
return a + b;
}
decltype(&func) funcPtr = &func; // funcPtr的类型是int (*)(int, int)
bind
基本语法
- callable:可以是函数指针、成员函数指针、函数对象或 lambda 表达式。
- arg1, arg2, ..., argN:指定参数,可以是具体的值,也可以是占位符(例如
std::placeholders::_1
- 占位符
在使用
std::bind
时,可以使用占位符来表示绑定函数的参数位置。占位符定义在std::placeholders
命名空间中,通常用_1
、_2
等表示。例如:std::placeholders::_1
表示绑定函数的第一个参数。std::placeholders::_2
表示绑定函数的第二个参数- 注意
- 参数绑定顺序:
std::bind
会按照传递给它的参数顺序进行绑定。占位符的编号必须与实际参数的位置相对应。- 捕获外部变量:使用
std::bind
时,可以将外部变量作为绑定参数传递,从而实现闭包效果。- 返回类型推导:
std::bind
创建的函数对象会自动推导返回类型,无需显式指定
绑定成员函数(常用)
#include <iostream>
#include <functional>
class MyClass {
public:
void print_message(const std::string& message) {
std::cout << "Message: " << message << std::endl;
}
};
int main() {
MyClass my_object;
// 绑定成员函数
auto bound_member_function = std::bind(&MyClass::print_message, &my_object, std::placeholders::_1);
bound_member_function("Hello, World!"); // 输出:Message: Hello, World!
return 0;
}
绑定函数
#include <iostream>
#include <functional>
void print_sum(int a, int b) {
std::cout << "Sum: " << a + b << std::endl;
}
int main() {
// 绑定参数到具体值
auto bound_print = std::bind(print_sum, 5, 3);
bound_print(); // 输出:Sum: 8
// 使用占位符
auto bound_print_with_placeholder = std::bind(print_sum, std::placeholders::_1, 10);
bound_print_with_placeholder(5); // 输出:Sum: 15
return 0;
}
绑定函数对象
#include <iostream>
#include <functional>
struct Multiply {
int operator()(int a, int b) const {
return a * b;
}
};
int main() {
Multiply multiply;
// 绑定函数对象
auto bound_multiply = std::bind(multiply, std::placeholders::_1, 4);
std::cout << "Product: " << bound_multiply(5) << std::endl; // 输出:Product: 20
return 0;
}
范围for
- 作用:遍历容器中的数据
- 参数
- declaration:用于声明每次迭代中从容器中提取的元素,可以是变量或引用。
- expression:表示需要遍历的容器或范围
- 注意点
- 自动推导类型,可以使用auto关键字自动推导元素类型
- 如果不需要修改容器中的元素,最后使用const声明元素,避免容器中的数据被修改
- 范围:任何可以使用begin 和 end 的容器
事例代码
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 使用引用遍历并修改元素
for (int& elem : vec) {
elem *= 2;
}
for (int elem : vec) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
右值引用
左值与右值
- 左值:持久存储的对象,通常是变量
- 右值:没有持久存储的临时对象,一般都是临时变量
用法:move可以显式的将一个左值转换为对应的右值引用类型
右值引用与移动语义结合事例代码
#include <iostream>
#include <vector>
#include <string>
class Resource {
public:
std::string name;
std::vector<int> data;
Resource(const std::string& name) : name(name) {
std::cout << "Constructing " << name << std::endl;
}
Resource(const Resource& other) : name(other.name), data(other.data) {
std::cout << "Copy Constructing " << name << std::endl;
}
Resource(Resource&& other) noexcept : name(std::move(other.name)), data(std::move(other.data)) {
std::cout << "Move Constructing " << name << std::endl;
}
Resource& operator=(Resource&& other) noexcept {
if (this != &other) {
name = std::move(other.name);
data = std::move(other.data);
std::cout << "Move Assigning " << name << std::endl;
}
return *this;
}
};
int main() {
Resource res1("Resource1");
Resource res2("Resource2");
// 触发移动构造函数
Resource res3 = std::move(res1);
// 触发移动赋值运算符
res2 = std::move(res3);
return 0;
}
标准库move函数
用法总结
- 作用:将参数显式的转换为一个右值引用,使对象可以通过移动而不是复制的方式传递或者返回,从而提高程序性能(例如在动态内存等场景中,让资源空间移动,可以避免复制开辟空间的性能消耗)
- 理解:提供一个移动资源的方法
- 实现:将其参数转换为右值引用
使用代码事例
#include <iostream>
#include <vector>
#include <utility> // for std::move
int main() {
std::vector<int> v1 = { 1, 2, 3, 4, 5 };
std::vector<int> v2;
// 使用 std::move 将 v1 转换为右值引用,从而启用移动语义
v2 = std::move(v1);
// 现在 v1 应该是空的,v2 持有原先 v1 的资源
std::cout << "v1 size: " << v1.size() << std::endl;
std::cout << "v2 size: " << v2.size() << std::endl;
return 0;
}
如果想要对象支持移动语义,则需要的为其定义移动构造函数以及移动赋值运算符
class MyClass {
public:
MyClass() : data(new int[100]) {}
~MyClass() { delete[] data; }
// 移动构造函数
MyClass(MyClass&& other) noexcept : data(other.data) {
other.data = nullptr;
}
// 移动赋值运算符
MyClass& operator=(MyClass&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
other.data = nullptr;
}
return *this;
}
// 禁用复制构造函数和复制赋值运算符
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;
private:
int* data;
};
禁用对象默认函数
C++11中如果不希望对象被复制,可以将复制构造函数以及复制赋值函数声明为delete来禁用,移动构造和移动赋值同样适用。C++11之前,可以将对象的默认函数放入私有域中,从而达到禁止使用默认函数的功能。
代码事例:C++11中的做法,以及private禁止默认函数的做法
class MyClass {
public:
MyClass() = default;
~MyClass() = default;
// 禁用复制构造函数
MyClass(const MyClass&) = delete;
// 禁用复制赋值运算符
MyClass& operator=(const MyClass&) = delete;
// 禁用移动构造函数
MyClass(MyClass&&) = delete;
// 禁用移动赋值运算符
MyClass& operator=(MyClass&&) = delete;
};
class MyClass {
public:
MyClass() {}
~MyClass() {}
private:
// 禁用复制构造函数
MyClass(const MyClass&);
// 禁用复制赋值运算符
MyClass& operator=(const MyClass&);
// 禁用移动构造函数
MyClass(MyClass&&);
// 禁用移动赋值运算符
MyClass& operator=(MyClass&&);
};
constexpr
该关键字用于指示表达式或者函数在编译的时候求职,用于定义常量表达式,从而提高程序的效率和安全性。
constexpr变量
- 编译时求值的常量
- constexpr确保在编译时进行求值,而const只可以确保值不能在运行中改变
constexpr int square(int x) {
return x * x;
}
int main() {
constexpr int a = 10;
constexpr int b = square(a); // 在编译时求值
int arr[b]; // 使用编译时常量作为数组大小
return 0;
}
constexpr函数
- 该函数是在编译时求值
- 使用条件
- 函数的返回类型以及参数必须是字面值类型
- 函数体中只可以包含单一的return语句(注意,该规定在C++14中更改了)
constexpr int factorial(int n) {
return (n <= 1) ? 1 : (n * factorial(n - 1));
}
int main() {
constexpr int result = factorial(5); // 在编译时计算 5 的阶乘
static_assert(result == 120, "Factorial calculation is incorrect");
return 0;
}
constexpr 与 const的区别
- const确保变量在运行的时候是只读的,但不一定在编译的时候求值
- constexpr确保表达式在编译时求值,并且变量在运行的时候是只读
const int a = 10; // 运行时常量
constexpr int b = 10; // 编译时常量
const int x = factorial(a); // 可以在运行时计算
constexpr int y = factorial(b); // 必须在编译时计算
初始化列表 initializer list
用法总结
- 语法使用:直接初始化成员变量,而不用在构造函数体内赋值
- 特点
- 优化性能,减少性能开销
- const成员变量必须通过初始化列表进行初始化,因为const成员变量在构造完成后不可以被赋值
- 引用成员也必须通过初始化列表进行初始化
- 顺序列表中的初始化的顺序必须和类中声明顺序一致
class MyClass {
public:
MyClass(int a, int b) : x(a), y(b), z(a + b), ref(a) {}
private:
const int x;
int y;
int z;
int& ref;
};
nullptr
用法总结
- 作用:C++11之前使用NULL宏来表示,NULL宏被定义为整数0,容易在上下文中引起歧义;nullptr则是一个指针字面值,专门用于表示空指针,不存在歧义问题
- 优点
- 类型安全:nullptr是一个指针类型,所以不会与整数类型混淆
- 函数重载的时候,nullptr可以帮忙区分指针和整数参数
C++14
函数返回值类型推导
- 作用:允许编译器根据函数返回语句,自动推导出返回类型,下面案例是自动推导出int类型
- 注意
- 单一返回类型:函数中所有返回语句必须是相同类型,否则编译器会报错
- 返回引用和指针的时候,需要确保返回的对象在作用域中是否有效,避免悬空引用
#include <iostream>
auto add(int a, int b) {
return a + b;
}
int main() {
std::cout << add(2, 3) << std::endl; // 输出 5
return 0;
}
lambda函数形参允许泛型
作用:无需预先指定参数类型,编译器根据lambda的使用情况自动推导参数的类型
事例案例理解
#include <iostream>
int main() {
auto add = [](auto x, auto y) {
return x + y;
};
std::cout << add(1, 2) << std::endl; // 输出 3
std::cout << add(1.5, 2.5) << std::endl; // 输出 4.0
std::cout << add(std::string("Hello, "), std::string("World!")) << std::endl; // 输出 Hello, World!
return 0;
}
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
int factor = 2;
auto multiply = [factor](auto x) {
return x * factor;
};
std::cout << multiply(3) << std::endl; // 输出 6
std::cout << multiply(3.5) << std::endl; // 输出 7.0
return 0;
}
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
auto print = [](const auto& container) {
for (const auto& elem : container) {
std::cout << elem << " ";
}
std::cout << std::endl;
};
print(vec); // 输出 1 2 3 4 5
return 0;
}
变量模板
作用:类似于函数模板,但是是给变量使用的一种模板
#include <iostream>
template<typename T>
constexpr T pi = T(3.1415926535897932385);
int main() {
std::cout << pi<float> << std::endl; // 输出 3.14159
std::cout << pi<double> << std::endl; // 输出 3.141592653589793
std::cout << pi<long double> << std::endl; // 输出 3.1415926535897932385
return 0;
}
常量模版,事例中是获取不同类型的最大值
#include <iostream>
template<typename T>
constexpr T max_value = std::numeric_limits<T>::max();
int main() {
std::cout << max_value<int> << std::endl; // 输出 int 类型的最大值
std::cout << max_value<unsigned int> << std::endl; // 输出 unsigned int 类型的最大值
std::cout << max_value<double> << std::endl; // 输出 double 类型的最大值
return 0;
}
属性模版,下面事例展示用于计算的给定类型的平方根
#include <iostream>
#include <cmath>
template<typename T>
constexpr T square(T x) {
return x * x;
}
template<typename T>
constexpr T sqrt_value = std::sqrt(square(T(2)));
int main() {
std::cout << sqrt_value<float> << std::endl; // 输出 2.82843
std::cout << sqrt_value<double> << std::endl; // 输出 2.82843
std::cout << sqrt_value<long double> << std::endl; // 输出 2.82843
return 0;
}
类模版和变量模板
#include <iostream>
template<typename T>
struct Traits {
static constexpr T zero = T(0);
};
int main() {
std::cout << Traits<int>::zero << std::endl; // 输出 0
std::cout << Traits<float>::zero << std::endl; // 输出 0
std::cout << Traits<double>::zero << std::endl; // 输出 0
return 0;
}
deprecated属性
作用:标记不建议使用的函数、变量或者类型,如果使用了,则编译的时候会报错
标记函数被弃用
#include <iostream>
[[deprecated]]
void oldFunction() {
std::cout << "This is an old function." << std::endl;
}
void newFunction() {
std::cout << "This is a new function." << std::endl;
}
int main() {
oldFunction(); // 编译时会产生警告
newFunction(); // 正常调用
return 0;
}
标记变量弃用
#include <iostream>
[[deprecated("Use newVar instead")]]
int oldVar;
int newVar;
int main() {
oldVar = 10; // 编译时会产生警告
newVar = 20; // 正常使用
std::cout << oldVar << std::endl;
std::cout << newVar << std::endl;
return 0;
}
标记类型弃用
#include <iostream>
[[deprecated("Use NewStruct instead")]]
struct OldStruct {
int value;
};
struct NewStruct {
int value;
};
int main() {
OldStruct oldInstance; // 编译时会产生警告
oldInstance.value = 10;
NewStruct newInstance;
newInstance.value = 20;
std::cout << oldInstance.value << std::endl;
std::cout << newInstance.value << std::endl;
return 0;
}
标记类的成员函数被弃用
#include <iostream>
class MyClass {
public:
[[deprecated("Use newMethod() instead")]]
void oldMethod() {
std::cout << "This is the old method." << std::endl;
}
void newMethod() {
std::cout << "This is the new method." << std::endl;
}
};
int main() {
MyClass obj;
obj.oldMethod(); // 编译时会产生警告
obj.newMethod(); // 正常调用
return 0;
}
std::make_unique
- 作用:用于创建std::unique_ptr对象,以一种更安全高效的方式创建智能指针,避免new的内存泄漏风险
- 优点
- 避免内存泄漏
- 类型安全:会自动推导出指针类型,减少类型错误的风险
- 语法分析
T
是要创建的对象的类型。Args&&... args
是传递给T
的构造函数的参数
创建一个对象:创建一个指向整数42的unique_ptr
#include <iostream>
#include <memory>
int main() {
auto p = std::make_unique<int>(42);
std::cout << *p << std::endl; // 输出 42
return 0;
}
创建动态数组
#include <iostream>
#include <memory>
int main() {
auto p = std::make_unique<int[]>(5);
for (int i = 0; i < 5; ++i) {
p[i] = i * i;
}
for (int i = 0; i < 5; ++i) {
std::cout << p[i] << " "; // 输出 0 1 4 9 16
}
std::cout << std::endl;
return 0;
}
创建自定义对象
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass(int x) : x_(x) {
std::cout << "MyClass constructed with " << x_ << std::endl;
}
~MyClass() {
std::cout << "MyClass destructed" << std::endl;
}
int getX() const { return x_; }
private:
int x_;
};
int main() {
auto p = std::make_unique<MyClass>(10);
std::cout << p->getX() << std::endl; // 输出 10
return 0;
}
C++17
结构化绑定
作用:通过简单的语法,实现将变量绑定到结构或者元祖的成员上
解构元组
#include <iostream>
#include <tuple>
std::tuple<int, double, std::string> getTuple() {
return {1, 2.5, "hello"};
}
int main() {
auto [x, y, z] = getTuple();
std::cout << "x: " << x << ", y: " << y << ", z: " << z << std::endl;
return 0;
}
解构结构体
#include <iostream>
struct Point {
int x;
int y;
};
int main() {
Point p{10, 20};
auto [x, y] = p;
std::cout << "x: " << x << ", y: " << y << std::endl;
return 0;
}
解构数组
#include <iostream>
int main() {
int arr[] = {1, 2, 3};
auto [a, b, c] = arr;
std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;
return 0;
}
与标准库结合使用
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> m = {{1, "one"}, {2, "two"}, {3, "three"}};
for (const auto& [key, value] : m) {
std::cout << "key: " << key << ", value: " << value << std::endl;
}
return 0;
}
if-switch语句初始化
含义:允许在if 和switch语句中进行初始化,从而让变量的作用域更加局部化(根据具体事例理解)
if语句初始化(auto it ...初始化,只在if语句范围内有用)
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
if (auto it = std::find(vec.begin, vec.end(), 3); it != vec.end()) {
std::cout << "Found 3 at index " << std::distance(vec.begin(), it) << std::endl;
} else {
std::cout << "3 not found" << std::endl;
}
return 0;
}
Switch语句初始化,也同样只在Switch语句范围内起作用
#include <iostream>
#include <map>
enum class Color { RED, GREEN, BLUE };
int main() {
std::map<std::string, Color> colorMap = {{"red", Color::RED}, {"green", Color::GREEN}, {"blue", Color::BLUE}};
std::string colorStr = "green";
switch (auto it = colorMap.find(colorStr); it != colorMap.end() ? it->second : Color::BLUE) {
case Color::RED:
std::cout << "Color is red" << std::endl;
break;
case Color::GREEN:
std::cout << "Color is green" << std::endl;
break;
case Color::BLUE:
std::cout << "Color is blue" << std::endl;
break;
default:
std::cout << "Unknown color" << std::endl;
break;
}
return 0;
}
constexpr lambda表达式
总结
- 该lambda表达式可以在编译的时候进行常量计算,从而提高代码效率和安全性。
- 注意
- 表达式限制: constexpr lambda表达式中的代码必须符合constexpr函数的要求
- 常量上下文:该lambda表达式必须能够在编译的时候计算
#include <iostream>
constexpr auto add = [](int a, int b) constexpr {
return a + b;
};
int main() {
constexpr int result = add(3, 4); // 在编译时计算
std::cout << "Result: " << result << std::endl; // 输出 7
return 0;
}
常量计算
#include <iostream>
#include <array>
constexpr auto square = [](int x) constexpr {
return x * x;
};
int main() {
constexpr int value = 5;
constexpr int result = square(value); // 在编译时计算
std::array<int, result> arr; // 使用计算结果作为数组大小
std::cout << "Array size: " << arr.size() << std::endl; // 输出 25
return 0;
}
与标准库结合使用的场景
#include <iostream>
#include <algorithm>
#include <array>
int main() {
constexpr auto greater = [](int a, int b) constexpr {
return a > b;
};
std::array<int, 5> arr = {5, 2, 3, 4, 1};
std::sort(arr.begin(), arr.end(), greater); // 在运行时排序
for (const auto& elem : arr) {
std::cout << elem << " "; // 输出 5 4 3 2 1
}
std::cout << std::endl;
return 0;
}
namespace嵌套
基于语法结构
#include <iostream>
namespace Outer {
int outerVar = 10;
namespace Inner {
int innerVar = 20;
class InnerClass {
public:
void display() {
std::cout << "Outer variable: " << outerVar << std::endl;
std::cout << "Inner variable: " << innerVar << std::endl;
}
};
}
}
int main() {
Outer::Inner::InnerClass obj;
obj.display();
return 0;
}
#include <iostream>
namespace Outer {
int outerVar = 10;
namespace Inner {
int innerVar = 20;
class InnerClass {
public:
void display() {
std::cout << "Outer variable: " << outerVar << std::endl;
std::cout << "Inner variable: " << innerVar << std::endl;
}
};
}
}
int main() {
Outer::Inner::InnerClass obj;
obj.display();
return 0;
}
std::any
总结
- 含义:提供一种类型安全的方式来存储和操作任意类型的值(可以理解成动态语言中的万能类型)
- 使用场景:用来代替一些需要存储不同类型对象的场景
- 事例代码
- 定义和使用any类型定义的数值
- 访问存储的数值
- 检查any中的类型(type())
#include <iostream>
#include <any>
int main() {
std::any a = 1; // 存储 int 类型
std::cout << std::any_cast<int>(a) << std::endl;
a = 3.14; // 存储 double 类型
std::cout << std::any_cast<double>(a) << std::endl;
a = std::string("Hello, std::any!"); // 存储 std::string 类型
std::cout << std::any_cast<std::string>(a) << std::endl;
return 0;
}
#include <iostream>
#include <any>
int main() {
std::any a = 10;
try {
int value = std::any_cast<int>(a);
std::cout << "Value: " << value << std::endl;
} catch (const std::bad_any_cast& e) {
std::cout << "Bad any_cast: " << e.what() << std::endl;
}
return 0;
}
#include <iostream>
#include <any>
#include <typeinfo>
int main() {
std::any a = 10;
if (a.type() == typeid(int)) {
std::cout << "a contains an int" << std::endl;
} else {
std::cout << "a does not contain an int" << std::endl;
}
return 0;
}
std::basic_string_view
总结
- 作用:轻量级、非拥有的字符串视图类型,对字符串只读访问,也就是只查看字符串指定内容,不需要复制或者拥有字符串的实际数据
- 特点
- 轻量级:只是一个视图,不拥有数据,所以构造和赋值的性能开销小
- 无拷贝:因为不需要数据,所以不会进行数据拷贝
- 灵活:可以按照自己的需要灵活拷贝字符串
#include <iostream>
#include <string_view>
void printString(std::string_view sv) {
std::cout << sv << std::endl;
}
int main() {
std::string str = "Hello, std::string_view!";
printString(str); // 传递 std::string
printString("Hello, world!"); // 传递字符串字面量
printString(str.substr(7, 12)); // 传递 std::string 的子字符串
return 0;
}
//基本操作
#include <iostream>
#include <string_view>
int main() {
std::string_view sv = "Hello, world!";
std::cout << "Length: " << sv.length() << std::endl; // 输出长度
std::cout << "First character: " << sv.front() << std::endl; // 输出第一个字符
std::cout << "Last character: " << sv.back() << std::endl; // 输出最后一个字符
std::cout << "Substring: " << sv.substr(7, 5) << std::endl; // 输出子字符串 "world"
// 查找子字符串
size_t pos = sv.find("world");
if (pos != std::string_view::npos) {
std::cout << "\"world\" found at position " << pos << std::endl;
}
return 0;
}
#include <iostream>
#include <string_view>
int main() {
std::string_view sv = "Hello, world!";
std::cout << "Length: " << sv.length() << std::endl; // 输出长度
std::cout << "First character: " << sv.front() << std::endl; // 输出第一个字符
std::cout << "Last character: " << sv.back() << std::endl; // 输出最后一个字符
std::cout << "Substring: " << sv.substr(7, 5) << std::endl; // 输出子字符串 "world"
// 查找子字符串
size_t pos = sv.find("world");
if (pos != std::string_view::npos) {
std::cout << "\"world\" found at position " << pos << std::endl;
}
return 0;
}
C和C++
相同点
- 基本语法结构,循环控制函数定义都类似
- 基本数据结构
- C中的标准库函数在C++中同样适用
- 指针与数组使用方法类似
- 预处理指令
不同点
- 面向对象:C++支持面向对象
- 函数与运算符重载:C++支持,C不支持
- 模板:C++支持
- C++支持STL标准库
- 两者的内存管理不同,C++有异常处理机制,但是C没有
Java和C++
相同点
面向对象编程:
- 两者都支持面向对象编程,包括类、继承、多态、封装等概念。
语法结构:
- 两者的基本语法结构相似,如
if-else
、for
循环、while
循环、switch
语句等基本数据类型:
- 都有基本数据类型,如
int
、char
、float
、double
等。标准库支持:
- 两者都提供了丰富的标准库,用于字符串操作、输入输出、集合等
不同点
内存管理:
- C++ 需要手动进行内存管理,使用
new
和delete
进行动态内存分配和释放。- Java 有垃圾回收机制,自动管理内存。
平台独立性:
- C++ 编译生成的可执行文件是平台相关的。
- Java 编译生成字节码,运行在 Java 虚拟机(JVM)上,实现平台独立性。
多重继承:
- C++ 支持多重继承。
- Java 不支持多重继承,但可以通过接口实现类似效果。
指针:
- C++ 直接支持指针。
- Java 不直接支持指针,提供了引用类型。
异常处理:
- Java 强制使用异常处理,许多库函数都会抛出异常。
- C++ 提供异常处理机制,但使用不如 Java 强制。
模板与泛型:
- C++ 使用模板实现泛型编程。
- Java 使用泛型,但类型信息在运行时会被擦除(类型擦除)
Python和C++
相同点
支持面向对象编程:
- 两者都支持面向对象编程。
丰富的标准库:
- 两者都提供了丰富的标准库支持多种功能
不同点
语法和易用性:
- Python 语法简单易读,强调代码的可读性。
- C++ 语法复杂,更加灵活,但也更容易出现错误。
性能:
- C++ 是编译型语言,性能高,适合对性能要求高的系统编程。
- Python 是解释型语言,性能较低,但开发速度快,适合快速开发和脚本编写。
内存管理:
- C++ 手动管理内存。
- Python 有自动垃圾回收机制。
类型系统:
- C++ 是静态类型语言,编译时检查类型。
- Python 是动态类型语言,运行时检查类型。
并发与并行:
- C++ 提供了多线程和多进程的并发机制。
- Python 也支持多线程和多进程,但由于全局解释器锁(GIL)的存在,多线程性能受限,多进程更常用。
应用领域:
- C++ 常用于系统编程、游戏开发、嵌入式系统等。
- Python 常用于数据分析、机器学习、Web 开发、自动化脚本等
Go和C++
相同点
- 编译型语言:源代码在执行前需要编译成可执行文件
- 编译时对类型进行检查
- 支持标准库,比如文件操作或者网络编程等
不同点
- 语法
- Go语法简洁,代码可读性高
- C++语法复杂,功能强大,但是容易导致代码冗长难以维护
- 内存管理
- Go有垃圾回收机制,自动管理内存
- C++需要手动管理内存
- 并发编程
- Go内置强大的并发支持,可以通过goroutines和channels实现并发
- C++的多线程编程较为麻烦
- 编译时间
- Go变异速度快,适合快速开发和部署
- C++编译速度慢
错误处理:
- Go 使用显式错误处理,通过返回值和
error
类型处理错误。- C++ 使用异常处理机制,通过
try-catch
块捕获和处理异常。面向对象编程:
- C++ 是面向对象语言,支持类、继承、多态等特性。
- Go 支持面向对象编程,但没有类和继承,使用结构体和接口实现类似功能
Rust和C++
相同点
- 编译型语言,代码执行前都需要编译成可执行文件
- 强类型检查,编译的时候都会进行类型检查
- 都对底层硬件和内存进行精细控制,适合系统编程和高性能应用
不同点
- 内存管理
- C++需要程序员管理内存,通常需要使用new和delete进行动态内存分配和释放
- Rust则使用所有权系统和借助检查器管理内存,编译的时候保证内存安全,无需垃圾回收
- 并发编程
- Rust提供了安全的并发编程,通过所有权和类型系统在编译的时候防止数据竞争
- C++支持多线程编程,但是需要使用更复杂的线程库和同步机制
- 错误处理
- Rust使用显式错误处理,通过Result和Option类型来处理错误
- C++使用异常处理机制,通过try-catch模块来捕捉异常
- 面向对象
- C++支持面向对象语言,支持类、继承、多态等
- Rust则不支持传统的面向对象编程,但是提供结构体和特征来实现类似功能