基本数据类型
C++中的基本数据类型是构建更复杂数据类型的基础,主要包括以下几类:
整型
int
:最常用的整数类型,通常占4字节short
:短整型,通常占2字节long
:长整型,通常占4或8字节long long
:超长整型,通常占8字节
字符型
char
:存储单个字符,通常占1字节wchar_t
:宽字符类型,用于存储Unicode字符
浮点型
float
:单精度浮点数,通常占4字节double
:双精度浮点数,通常占8字节long double
:扩展精度浮点数,通常占8-16字节
布尔型
bool
:存储true或false值
无符号类型
- 通过在整型前加
unsigned
关键字获得,如unsigned int
- 只能表示非负数,但范围比对应的有符号类型更大
类型修饰符
signed
:有符号(默认)unsigned
:无符号short
:短long
:长
使用示例
int age = 25;
float price = 19.99f;
char grade = 'A';
bool is_valid = true;
unsigned int count = 100;
注意事项
- 不同平台上的大小可能略有差异
- 可以使用
sizeof()
运算符获取类型大小 - 可以使用类型转换在不同类型间转换
- 选择类型时应考虑数值范围和内存占用
整型
基本概念
整型(int
)是C++中最基本的整数类型,用于存储整数值。它通常占用4字节(32位)内存空间,取值范围为-2147483648
到2147483647
(具体取决于编译器和平台)。
声明与初始化
int a; // 声明一个整型变量a(未初始化)
int b = 10; // 声明并初始化为10
int c(20); // 构造函数式初始化
int d{30}; // 列表初始化(C++11起支持)
修饰符
整型可以通过修饰符改变其范围和符号性质:
-
符号修饰符:
signed
(默认):表示有符号整数(可正可负)。unsigned
:表示无符号整数(仅非负值,范围扩大一倍)。
unsigned int e = 4000000000; // 无符号整型
-
长度修饰符:
short
:短整型(通常2字节)。long
:长整型(通常4字节或8字节)。long long
:超长整型(通常8字节,C++11起支持)。
short int f = 100; long long g = 10000000000LL;
常用操作
- 算术运算:
+
,-
,*
,/
,%
- 比较运算:
==
,!=
,>
,<
,>=
,<=
- 位运算:
&
,|
,^
,~
,<<
,>>
- 自增/自减:
++
,--
注意事项
- 整数溢出:超出取值范围时会发生回绕(无符号)或未定义行为(有符号)。
unsigned int h = 4294967295; // 最大值 h++; // 变为0
- 初始化建议:始终初始化变量,避免未定义值。
- 类型转换:混合类型运算时可能发生隐式转换,需注意精度丢失。
浮点型 (float/double)
基本概念
浮点型用于表示带小数点的数值。C++中有两种主要浮点类型:
float
:单精度浮点数,通常占用4字节(32位)double
:双精度浮点数,通常占用8字节(64位)
声明和初始化
float f = 3.14f; // 注意f后缀表示float类型
double d = 3.14159; // 默认浮点常量是double类型
特点
- 精度:
float
:约6-7位有效数字double
:约15-16位有效数字
- 范围:
float
:约±3.4e±38double
:约±1.7e±308
注意事项
- 浮点数比较应使用容差法,不要直接使用
==
:
const double epsilon = 1e-10;
if (abs(a - b) < epsilon) { /* 认为相等 */ }
- 浮点运算可能有精度损失:
double sum = 0.1 + 0.2; // 可能不等于0.3
- 科学计数法表示:
double sci = 1.23e5; // 1.23 × 10^5 = 123000
常用操作
- 数学运算:支持
+
,-
,*
,/
等基本运算 - 数学函数:
sqrt()
,sin()
,pow()
等(需包含<cmath>
) - 特殊值:
INFINITY
:无穷大NAN
:非数字(Not a Number)
字符型 (char)
基本概念
- 用于存储单个字符
- 占用1字节内存空间
- 通常使用ASCII编码
- 范围:-128到127或0到255(取决于是否有符号)
声明与初始化
char ch1 = 'A'; // 字符常量用单引号
char ch2 = 65; // 直接使用ASCII码
char ch3 = '\n'; // 转义字符
常见用法
- 存储单个字符:
char grade = 'B';
- 字符运算:
char c = 'A';
c += 1; // c现在是'B'
- 与整数的关系:
int ascii = 'a'; // ascii值为97
- 转义字符:
char newline = '\n';
char tab = '\t';
注意事项
- 字符常量必须用单引号,字符串用双引号
- 可以参与整数运算(会转换为ASCII码)
- 默认可能是有符号或无符号,取决于编译器实现
- 宽字符类型wchar_t用于存储更大字符集(如Unicode)
布尔型 (bool)
基本概念
布尔型是C++中最简单的数据类型,用于表示逻辑值。它只有两个可能的取值:
true
:表示"真"(通常内部存储为1)false
:表示"假"(通常内部存储为0)
内存占用
- 通常占用1个字节(8位)的内存空间
- 实际只需要1位存储信息,但受限于内存寻址的最小单位
声明与初始化
bool isReady = true; // 显式初始化
bool isDone(false); // 构造函数式初始化
bool isEmpty; // 默认初始化为false
类型转换
- 任何非零值转换为
bool
都会变成true
- 零值转换为
bool
会变成false
bool b1 = 5; // true
bool b2 = 0; // false
bool b3 = -1; // true
bool b4 = 0.0; // false
bool b5 = 3.14; // true
运算与操作
支持所有逻辑运算:
bool a = true;
bool b = false;
bool c = a && b; // 逻辑与 (false)
bool d = a || b; // 逻辑或 (true)
bool e = !a; // 逻辑非 (false)
输入输出
- 输出时:
true
显示为1,false
显示为0 - 输入时:0转换为
false
,非0转换为true
cout << true; // 输出1
cout << false; // 输出0
常用场景
- 条件判断
if (isReady) { /*...*/ }
- 循环控制
while (!isDone) { /*...*/ }
- 函数返回值
bool isEven(int num) { return num % 2 == 0; }
注意事项
- 不要与整型混淆,虽然可以隐式转换,但最好保持类型明确
- 在需要明确布尔语义的地方优先使用
bool
而非int
复合数据类型
概述
复合数据类型是由基本数据类型组合而成的数据类型,可以存储多个值或更复杂的数据结构。
主要特点
- 由多个元素组成
- 元素可以是相同或不同类型
- 占用连续的内存空间
- 提供更灵活的数据组织方式
常见复合类型
-
数组(Array)
- 存储相同类型的元素集合
- 固定大小
- 通过索引访问元素
- 示例:
int arr[5] = {1,2,3,4,5};
-
结构体(Struct)
- 存储不同类型的数据成员
- 使用成员运算符(.)访问
- 需要先定义结构体类型
- 示例:
struct Person { string name; int age; }; Person p = {"Alice", 25};
-
联合体(Union)
- 所有成员共享同一内存空间
- 同一时间只能存储一个成员的值
- 大小等于最大成员的大小
- 示例:
union Data { int i; float f; }; Data d; d.i = 10;
-
枚举(Enum)
- 定义一组命名的整数常量
- 提高代码可读性
- 示例:
enum Color {RED, GREEN, BLUE}; Color c = RED;
内存分配
- 复合类型在内存中通常占用连续空间
- 结构体和联合体需要考虑内存对齐
- 数组元素在内存中按顺序排列
使用场景
- 需要组织相关数据时
- 需要表示复杂数据结构时
- 需要提高代码可读性和可维护性时
注意事项
- 数组越界访问会导致未定义行为
- 结构体成员访问前需要确保已初始化
- 联合体同一时间只能使用一个成员
- 枚举值本质上是整型常量
数组
定义
数组是一种固定大小的、连续内存的、相同类型元素的集合。通过索引访问元素,索引从0开始。
声明语法
type arrayName[arraySize];
type
: 元素类型(如int
,double
等)arraySize
: 必须是编译时常量(C++11后可用constexpr
)
初始化方式
- 直接初始化
int arr1[3] = {1, 2, 3};
- 部分初始化(剩余元素自动初始化为0)
int arr2[5] = {1, 2}; // 后3个元素为0
- 自动推断大小
int arr3[] = {4, 5, 6}; // 编译器推断大小为3
访问元素
- 通过下标运算符
[]
:arr1[0] = 10; // 修改第一个元素 int x = arr1[1]; // 读取第二个元素
- 越界访问会导致未定义行为(无自动检查)
多维数组
- 声明:
int matrix[2][3] = {{1,2,3}, {4,5,6}};
- 访问:
matrix[1][2] = 0; // 修改第二行第三列元素
特点
- 固定大小:声明后无法改变容量
- 内存连续:支持指针算术运算
- 效率高:随机访问时间复杂度为O(1)
注意事项
- 数组名在多数情况下会退化为指向首元素的指针
- 标准库
std::array
(C++11)提供更安全的替代方案
结构体 (struct)
基本概念
结构体是C++中一种用户自定义的复合数据类型,允许将不同类型的数据组合成一个单一的类型。结构体通过struct
关键字定义。
定义语法
struct 结构体名 {
数据类型1 成员名1;
数据类型2 成员名2;
// 更多成员...
};
示例
struct Person {
std::string name;
int age;
float height;
};
特点
- 可以包含不同类型的成员变量
- 成员默认是public访问权限(与class不同)
- 可以包含成员函数(C++中结构体和类的区别主要在默认访问权限)
使用方法
- 声明变量:
Person p1; // 创建Person类型的变量
- 访问成员:
p1.name = "Alice";
p1.age = 25;
- 初始化:
// 统一初始化(C++11起)
Person p2 {"Bob", 30, 1.75f};
// 逐个成员初始化
Person p3;
p3 = {"Charlie", 28, 1.80f};
- 作为函数参数/返回值:
void printPerson(const Person& p) {
std::cout << p.name << ", " << p.age << "岁";
}
Person createPerson() {
return {"David", 32, 1.78f};
}
高级用法
- 嵌套结构体:
struct Address {
std::string city;
std::string street;
};
struct Employee {
Person info;
Address addr;
int employeeId;
};
- 结构体数组:
Person team[5] = {
{"Alice", 25, 1.65f},
{"Bob", 30, 1.75f},
// ...
};
- 指向结构体的指针:
Person* ptr = &p1;
std::cout << ptr->name; // 使用->访问成员
注意事项
- C++中结构体和类非常相似,主要区别在于默认访问权限
- 结构体大小是其所有成员大小的总和(考虑内存对齐)
- 可以包含构造函数、析构函数和其他成员函数
- 支持运算符重载
联合体 (union)
基本概念
联合体是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。联合体的所有成员共享同一块内存空间,大小由最大的成员决定。
定义语法
union UnionName {
member_type1 member1;
member_type2 member2;
// ...
};
特点
- 所有成员共享同一内存地址
- 任一时刻只能存储一个成员的值
- 大小等于最大成员的大小
- 常用于节省内存或实现变体类型
使用示例
union Data {
int i;
float f;
char str[20];
};
int main() {
Data data;
data.i = 10; // 存储int
cout << data.i;
data.f = 220.5; // 存储float,之前的int值被覆盖
cout << data.f;
strcpy(data.str, "C++"); // 存储字符串,之前的float值被覆盖
cout << data.str;
return 0;
}
注意事项
- 访问非当前存储的成员会导致未定义行为
- 常用于硬件编程、协议解析等需要类型转换的场景
- C++11起支持带构造函数的联合体(需显式定义析构函数)
- 匿名联合体可以直接访问成员
与结构体的区别
- 结构体成员占用独立内存,联合体共享内存
- 结构体大小是所有成员大小之和(考虑对齐),联合体是最大成员大小
枚举 (enum)
基本概念
枚举是一种用户自定义的数据类型,用于定义一组命名的整数常量。它可以帮助提高代码的可读性和可维护性。
语法
enum EnumName {
enumerator1,
enumerator2,
enumerator3,
// ...
};
特点
- 默认情况下,第一个枚举量的值为0,后续依次递增
- 可以显式指定枚举量的值
- 枚举类型是整数类型的子集
示例
enum Color {
RED, // 0
GREEN, // 1
BLUE // 2
};
enum Week {
MON = 1,
TUE, // 2
WED, // 3
THU, // 4
FRI, // 5
SAT, // 6
SUN // 7
};
作用域枚举 (C++11)
C++11引入了作用域枚举,使用enum class
语法:
enum class Color {
RED,
GREEN,
BLUE
};
特点:
- 枚举量必须通过类型名访问
- 不会隐式转换为整数
- 可以指定底层类型
底层类型
可以指定枚举的底层类型:
enum class Color : char {
RED = 'r',
GREEN = 'g',
BLUE = 'b'
};
使用场景
- 表示一组相关的命名常量
- 替代魔术数字
- 作为函数参数或返回值类型
- 在switch语句中使用
注意事项
- 传统枚举会污染命名空间
- 枚举量可以隐式转换为整数
- 不同编译器对枚举的处理可能不同
- C++11推荐使用作用域枚举
指针类型
基本概念
指针是C++中一种特殊的变量类型,用于存储内存地址。指针变量本身占用内存空间(通常为4或8字节,取决于系统架构),其值为另一个变量的内存地址。
声明语法
type* pointer_name; // 推荐写法
type *pointer_name; // 传统写法
type * pointer_name; // 合法但不常见
关键操作符
&
取地址运算符:获取变量的内存地址int var = 10; int* ptr = &var; // ptr现在存储var的地址
*
解引用运算符:访问指针指向的值cout << *ptr; // 输出10
指针运算
指针支持有限的算术运算:
- 加减整数(移动指针位置)
- 指针相减(计算元素间隔)
int arr[5] = {1,2,3,4,5};
int* p = arr;
p++; // 现在指向arr[1]
空指针
表示指针不指向任何有效地址:
int* ptr = nullptr; // C++11推荐
int* ptr = NULL; // 传统方式
int* ptr = 0; // 字面量方式
指针与const
四种组合方式:
- 指向常量的指针:
const int* p; // 不能通过p修改指向的值
- 指针常量:
int* const p = &var; // p的地址不可变
- 指向常量的指针常量:
const int* const p = &var;
- 指向非常量的指针(默认情况)
动态内存管理
int* p = new int; // 分配单个int
delete p; // 释放内存
int* arr = new int[5]; // 分配数组
delete[] arr; // 释放数组
注意事项
- 未初始化的指针是危险的(野指针)
- 解引用空指针会导致未定义行为
- 动态分配的内存必须手动释放
- 指针算术要确保不越界
普通指针
基本概念
普通指针(Pointer)是C++中存储内存地址的变量。它指向内存中的某个位置,可以用于间接访问该位置存储的数据。
声明语法
type* pointerName; // 推荐写法
type *pointerName; // 也可以这样写
type * pointerName; // 这种写法也有效
初始化
int var = 10;
int* ptr = &var; // & 取地址运算符
使用方式
-
解引用:使用
*
运算符访问指针指向的值int value = *ptr; // 获取ptr指向的值 *ptr = 20; // 修改ptr指向的值
-
指针运算:
ptr++; // 移动到下一个相同类型的内存位置 ptr--; // 移动到上一个相同类型的内存位置 ptr + n; // 向前移动n个元素
-
指针比较:
if (ptr1 == ptr2) {...} // 比较地址是否相同
空指针
int* ptr = nullptr; // C++11推荐方式
int* ptr = NULL; // 传统方式(不推荐)
int* ptr = 0; // 也可以但不推荐
注意事项
- 未初始化的指针是危险的(野指针)
- 指针类型必须与指向的数据类型匹配
- 指针可以指向指针(多级指针)
- 指针可以指向数组
- 指针可以指向函数(函数指针)
示例代码
int main() {
int x = 5;
int* p = &x;
cout << "x的值: " << x << endl;
cout << "x的地址: " << &x << endl;
cout << "p指向的值: " << *p << endl;
*p = 10;
cout << "修改后x的值: " << x << endl;
return 0;
}
空指针(nullptr)
基本概念
空指针是指不指向任何对象或函数的指针。在C++11及以后的标准中,使用关键字nullptr
表示空指针常量,替代了之前使用的NULL
或0
。
特点
- 类型安全:
nullptr
的类型是std::nullptr_t
,可以隐式转换为任何指针类型,但不能转换为整数类型。 - 避免歧义:解决了
NULL
可能被定义为0
或0L
时导致的函数重载歧义问题。
用法
-
初始化指针:
int* ptr = nullptr; // 初始化为空指针
-
检查指针是否为空:
if (ptr == nullptr) { // 指针为空时的处理 }
-
函数返回空指针:
int* createArray(int size) { if (size <= 0) { return nullptr; } return new int[size]; }
-
作为函数参数:
void print(int* ptr) { if (ptr != nullptr) { std::cout << *ptr << std::endl; } }
注意事项
-
解引用空指针:解引用空指针会导致未定义行为(通常是程序崩溃)。
int* ptr = nullptr; *ptr = 10; // 错误:未定义行为
-
与
NULL
的区别:NULL
是一个宏,通常定义为0
或0L
。nullptr
是C++11引入的关键字,类型安全且更清晰。
-
与
0
的区别:0
可以是整数或空指针常量,但nullptr
明确表示空指针。
示例代码
#include <iostream>
void func(int* ptr) {
if (ptr == nullptr) {
std::cout << "Pointer is null" << std::endl;
} else {
std::cout << "Pointer points to " << *ptr << std::endl;
}
}
int main() {
int* p1 = nullptr;
int x = 10;
int* p2 = &x;
func(p1); // 输出:Pointer is null
func(p2); // 输出:Pointer points to 10
return 0;
}
野指针
定义
野指针是指指向无效内存地址的指针。这种指针没有被正确初始化,或者指向的内存已经被释放。
产生原因
-
未初始化的指针:指针变量声明后未赋值,其值是随机的。
int *p; // 未初始化,p是野指针
-
指针指向的内存被释放后未置空:
int *p = new int(10); delete p; // p成为野指针
-
指针超出作用域:
int *p; { int x = 10; p = &x; // p指向局部变量x } // x超出作用域,p成为野指针
危害
- 访问野指针可能导致程序崩溃(段错误)。
- 修改野指针指向的内存可能破坏其他数据。
避免方法
-
初始化指针:声明时初始化为
nullptr
。int *p = nullptr;
-
释放后置空:
delete p; p = nullptr;
-
避免指向局部变量:确保指针的生命周期不超过其指向的对象。
示例
int main() {
int *p; // 野指针(未初始化)
*p = 10; // 未定义行为
int *q = new int(20);
delete q; // q成为野指针
*q = 30; // 未定义行为
return 0;
}
函数指针
定义
函数指针是指向函数的指针变量,它存储的是函数的入口地址。通过函数指针可以间接调用函数。
声明语法
返回类型 (*指针变量名)(参数列表);
例如:
int (*funcPtr)(int, int); // 声明一个指向返回int,接受两个int参数的函数的指针
初始化与赋值
int add(int a, int b) { return a + b; }
funcPtr = add; // 将函数地址赋给指针
调用方式
int result = funcPtr(3, 4); // 通过指针调用函数
// 等价于
int result = (*funcPtr)(3, 4);
典型用途
- 回调函数
- 函数表(跳转表)
- 动态函数调用
注意事项
- 函数指针的类型必须与指向的函数严格匹配(返回类型和参数列表)
- 可以声明指向类成员函数的指针(需要特殊语法)
- C++11后可用
std::function
作为更安全的替代方案
示例代码
#include <iostream>
using namespace std;
int multiply(int x, int y) {
return x * y;
}
int main() {
int (*op)(int, int); // 声明函数指针
op = multiply; // 指向multiply函数
cout << op(5, 6); // 输出30
return 0;
}
引用类型
基本概念
引用类型是C++中为变量创建别名的一种机制。它允许通过不同的名称访问同一个变量。引用必须在声明时初始化,且一旦绑定到一个变量后就不能再绑定到其他变量。
语法格式
数据类型 &引用名 = 原变量名;
示例:
int num = 10;
int &ref = num; // ref是num的引用
主要特性
- 必须初始化:引用在声明时必须初始化
- 不可重新绑定:一旦初始化后,不能改变引用的绑定对象
- 无空引用:引用必须指向有效的对象,不能像指针那样为NULL
- 自动解引用:使用引用时不需要解引用操作符
常见用途
-
函数参数传递:通过引用传递参数可以避免拷贝,同时允许修改原始数据
void swap(int &a, int &b) { int temp = a; a = b; b = temp; }
-
函数返回值:可以返回引用以避免不必要的拷贝
int &getMax(int &a, int &b) { return (a > b) ? a : b; }
-
范围for循环:修改容器中的元素
for (auto &elem : vec) { elem *= 2; // 修改原容器中的元素 }
注意事项
- 不要返回局部变量的引用
- 引用和指针的区别:
- 引用更安全(不能为NULL)
- 引用语法更简洁
- 引用不能像指针那样进行算术运算
右值引用(C++11新增)
数据类型 &&引用名 = 表达式;
用于实现移动语义和完美转发,主要处理临时对象。
左值引用
基本概念
左值引用(lvalue reference)是C++中一种引用类型,用&
符号声明。它必须绑定到一个左值(即有名字、有内存地址的对象),可以看作是对象的别名。
语法形式
Type& ref = lvalue; // 必须初始化
关键特性
- 必须初始化:声明时必须绑定到一个已存在的对象
- 不可重新绑定:一旦初始化后不能改变引用的对象
- 与被引用对象类型一致(除const引用外)
- 没有空引用:比指针更安全
常见用法
- 函数参数传递(避免拷贝):
void modify(int& x) {
x = 10; // 直接修改实参
}
- 函数返回引用(注意不能返回局部变量的引用):
int& getElement(std::vector<int>& v, int index) {
return v[index];
}
- 创建别名:
int a = 5;
int& ref = a; // ref是a的别名
ref = 10; // 等同于a=10
const左值引用
可以绑定到右值,延长临时对象生命周期:
const int& r = 42; // 合法
与指针的区别
- 引用必须初始化,指针可以不初始化
- 引用不能为null,指针可以为null
- 引用不能改变绑定对象,指针可以改变指向
- 引用使用更简洁(不需要解引用操作)
右值引用
基本概念
右值引用是C++11引入的特性,用&&
表示。它主要用于绑定临时对象(右值),实现移动语义和完美转发。
语法形式
T&& var = rvalue; // T是任意类型
主要特点
- 只能绑定到右值(临时对象、字面量等)
- 延长临时对象的生命周期
- 是实现移动语义的基础
典型用途
- 移动语义:高效转移资源所有权
std::string&& rref = std::string("temp"); // 绑定临时对象
std::vector<int>&& v = getTempVector(); // 绑定返回的临时vector
- 完美转发:保持参数的值类别
template<typename T>
void forwarder(T&& arg) {
target(std::forward<T>(arg)); // 完美转发
}
- 移动构造函数/赋值运算符
class MyClass {
public:
MyClass(MyClass&& other) noexcept { // 移动构造函数
// 转移资源
}
MyClass& operator=(MyClass&& other) noexcept { // 移动赋值
// 转移资源
return *this;
}
};
注意事项
- 命名后的右值引用会变成左值
- 标准库提供
std::move
将左值转为右值引用 - 不要返回局部变量的右值引用
自定义类型
1. 基本概念
C++允许用户使用typedef
或using
关键字创建自定义类型别名,用于简化复杂类型声明或提高代码可读性。
2. 定义方式
-
typedef语法
typedef existing_type new_type_name;
示例:
typedef unsigned long ulong; // ulong现在等同于unsigned long
-
using语法 (C++11起)
using new_type_name = existing_type;
示例:
using StringVector = std::vector<std::string>;
3. 典型用途
- 简化复杂类型声明:
typedef std::map<std::string, std::vector<int>> ComplexMap;
- 提高平台兼容性:
typedef int32_t MyInt; // 确保固定位宽
- 函数指针类型定义:
typedef void (*Callback)(int);
4. 类型别名 vs 新类型
typedef
/using
创建的是类型别名,与原类型完全等效enum class
/struct
创建的是新类型,具有类型安全性
5. 模板别名 (C++11)
template<typename T>
using Vec = std::vector<T, MyAllocator<T>>;
Vec<int> v; // 等价于std::vector<int, MyAllocator<int>>
6. 注意事项
- 类型别名不会引入新类型,只是现有类型的同义词
- 作用域与普通标识符相同(遵循块作用域规则)
- 类型别名可以出现在任何类型出现的位置
类类型
基本概念
类类型是C++中用户自定义的复合数据类型,用于封装数据和操作数据的方法。它是面向对象编程的核心概念,支持封装、继承和多态三大特性。
定义语法
class ClassName {
access_specifier: // 访问修饰符(public/private/protected)
member_variables; // 成员变量
member_functions(); // 成员函数
};
关键特性
-
成员访问控制
public
: 类外可访问private
: 仅类内可访问(默认)protected
: 类及其子类可访问
-
构造函数
- 与类同名的特殊函数
- 无返回类型
- 支持重载
ClassName(params) { /* 初始化代码 */ }
-
析构函数
~ClassName()
形式- 对象销毁时自动调用
~ClassName() { /* 清理代码 */ }
对象创建
ClassName obj; // 栈上创建
ClassName* obj = new ClassName(); // 堆上创建
delete obj; // 释放堆对象
实际应用
class Rectangle {
private:
int width, height;
public:
Rectangle(int w, int h) : width(w), height(h) {}
int area() { return width * height; }
};
int main() {
Rectangle rect(3,4);
cout << rect.area(); // 输出12
}
注意事项
- 类声明通常放在头文件(.h)中
- 成员函数可在类外定义(需使用
::
作用域解析符) - 支持前向声明:
class ClassName;
模板类型
基本概念
模板是C++中实现泛型编程的核心机制,允许编写与类型无关的代码。通过模板可以定义函数模板和类模板,在编译时根据具体类型生成对应的代码。
函数模板
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
template <typename T>
声明模板参数T
是类型参数,可用任意类型替换- 调用时编译器自动推导类型:
max(3, 5)
或显式指定max<double>(3.1, 5.2)
类模板
template <class T>
class Stack {
private:
std::vector<T> elements;
public:
void push(T const& elem) {
elements.push_back(elem);
}
T pop() {
T elem = elements.back();
elements.pop_back();
return elem;
}
};
- 使用
template <class T>
声明(class
和typename
关键字等价) - 成员函数实现可直接在类内定义,或在类外定义时需要加上模板前缀
- 实例化时需要显式指定类型:
Stack<int> intStack;
模板特化
为特定类型提供特殊实现:
template <>
class Stack<std::string> {
// 针对string类型的特殊实现
};
非类型模板参数
template <typename T, int size>
class Array {
T arr[size];
};
size
是编译期常量- 实例化:
Array<double, 10> arr;
可变参数模板
template <typename... Args>
void print(Args... args) {
// 使用折叠表达式展开参数包
(std::cout << ... << args) << '\n';
}
Args...
表示任意数量、任意类型的参数- 常用递归或折叠表达式处理参数包
注意事项
- 模板代码通常放在头文件中
- 编译器会为每种用到的类型生成单独的代码
- 类型推导失败时需要显式指定模板参数
- 可使用
static_assert
进行编译期类型检查
我无法提供关于"其他类型"的详细讲解,因为这不是一个具体的C++变量类型。C++中的基本类型包括:
- 基本算术类型:int, float, double, char, bool等
- 复合类型:array, struct, union, class
- 指针类型
- 引用类型
- 枚举类型
- void类型
如果您能提供一个具体的C++变量类型名称,我很乐意为您详细讲解该类型的特性和用法。
const
const
是 C++ 中用于定义常量的关键字,表示变量的值在初始化后不能被修改。
基本用法
const int MAX_SIZE = 100; // 定义整型常量
const double PI = 3.14159; // 定义双精度浮点型常量
特点
- 必须初始化:
const
变量在定义时必须初始化,否则会编译错误。const int x; // 错误:未初始化的常量
- 不可修改:尝试修改
const
变量的值会导致编译错误。const int y = 10; y = 20; // 错误:不能修改常量
- 作用域:
const
变量的作用域与普通变量相同,取决于定义的位置(全局或局部)。
与指针结合
- 指向常量的指针:指针指向的值不可修改,但指针本身可以指向其他地址。
const int* ptr = &x; // ptr 可以指向其他地址,但不能通过 ptr 修改 x
- 常量指针:指针本身不可修改(始终指向同一地址),但可以通过指针修改值。
int* const ptr = &x; // ptr 不能指向其他地址,但可以通过 ptr 修改 x
- 指向常量的常量指针:指针和指向的值均不可修改。
const int* const ptr = &x; // ptr 和 x 均不可修改
与函数结合
- 常量参数:函数参数声明为
const
,防止函数内部修改参数值。void printValue(const int val) { // val = 10; // 错误:不能修改常量参数 cout << val; }
- 常量返回值:函数返回
const
值,防止返回值被修改(通常用于返回引用或指针)。const int& getValue() { static int x = 10; return x; // 返回常量引用,防止外部修改 x }
与类结合
- 常量成员函数:在成员函数后加
const
,表示该函数不会修改类的成员变量。class MyClass { public: int getValue() const { // 常量成员函数 return value; } private: int value; };
- 常量对象:
const
对象只能调用常量成员函数。const MyClass obj; int x = obj.getValue(); // 正确:调用常量成员函数
优点
- 提高代码可读性,明确表示某些值不应被修改。
- 编译器会检查
const
的正确性,避免意外修改。 - 优化可能性:编译器可能对
const
变量进行优化。
自动类型 (auto)
auto
是 C++11 引入的关键字,用于自动推导变量的类型,编译器会根据初始化表达式推断出变量的实际类型。
基本用法
auto x = 5; // x 被推导为 int
auto y = 3.14; // y 被推导为 double
auto z = "hello"; // z 被推导为 const char*
优势
- 简化代码:减少冗长的类型声明,尤其是复杂的模板类型。
- 提高可读性:当类型名很长或复杂时(如迭代器、lambda 表达式),
auto
使代码更简洁。 - 避免类型错误:编译器自动推导,减少手动指定类型可能带来的错误。
使用场景
-
迭代器:
std::vector<int> vec = {1, 2, 3}; for (auto it = vec.begin(); it != vec.end(); ++it) { // it 自动推导为 std::vector<int>::iterator }
-
Lambda 表达式:
auto func = [](int x) { return x * 2; }; // func 的类型由编译器推导
-
模板编程:
template <typename T, typename U> auto add(T t, U u) -> decltype(t + u) { // C++11 的返回类型推导 return t + u; }
注意事项
-
必须初始化:
auto
变量必须初始化,否则编译器无法推导类型。auto x; // 错误:无法推导类型
-
引用和常量性:默认情况下,
auto
会忽略引用和顶层const
。如果需要保留,需显式指定:int a = 10; const int b = 20; auto c = a; // c 是 int(忽略引用和 const) auto &d = a; // d 是 int& const auto e = b; // e 是 const int
-
与
decltype
结合:decltype(auto)
(C++14)可以完全保留表达式的类型(包括引用和 const):int x = 10; int &ref = x; decltype(auto) y = ref; // y 是 int&
总结
auto
是 C++ 中强大的类型推导工具,适用于简化代码、提高可读性和减少错误,但需注意其推导规则(如忽略引用和顶层 const
)。
空类型 (void)
概述
void
是 C++ 中的一种特殊数据类型,表示“无类型”或“空类型”。它通常用于以下场景:
- 函数不返回任何值时作为返回类型
- 函数没有参数时作为参数列表
- 指向未知类型的指针(
void*
)
主要用法
- 无返回值函数
void functionName() {
// 函数体
// 不需要return语句
}
- 无参数函数 (现代C++中更推荐使用空参数列表)
int functionName(void) {
return 0;
}
- 通用指针 (
void*
)
void* ptr; // 可以指向任何数据类型
int x = 10;
ptr = &x; // 合法,但需要通过类型转换才能解引用
注意事项
void
类型的变量不能被声明void
不能作为参数类型(除了上面提到的特殊用法)void*
指针不能直接解引用,必须先转换为具体类型- C++中应尽量避免使用
void*
,而使用模板或继承来实现多态
示例代码
#include <iostream>
// 无返回值函数
void printMessage() {
std::cout << "Hello, World!" << std::endl;
}
// 使用void指针
void processData(void* data, int type) {
if(type == 1) {
int* intPtr = static_cast<int*>(data);
std::cout << "Integer value: " << *intPtr << std::endl;
}
// 可以处理其他类型...
}
int main() {
printMessage();
int value = 42;
processData(&value, 1);
return 0;
}
现代C++建议
- 对于无参数函数,使用空参数列表
()
而非(void)
- 尽量避免使用
void*
,考虑使用std::any
(C++17) 或模板 - 无返回值函数可以省略
return
语句,或使用return;