一、C++ 头文件
1. 面向对象基础
- 对象:在 C++ 中,对象是类的实例化。它结合了 C 语言的数据(属性)和 C 语言操作函数(方法)。例如,定义一个简单的
Person
类:
class Person {
private:
std::string name;
int age;
public:
Person(std::string n, int a) : name(n), age(a) {}
void introduce() {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
}
};
这里 Person
类封装了数据成员 name
和 age
,以及成员函数 introduce
。通过创建 Person
类的对象,就可以调用其成员函数进行操作。
2. 兼容 C 语言头文件
C++ 兼容 C 语言头文件,有些有自己的风格(无后缀),部分 C 语言头文件加 c
前缀(如 cstdio
对应 C 语言的 stdio.h
)。例如使用 C 语言风格的输入输出函数:
#include <cstdio>
int main() {
int num = 10;
printf("The number is %d\n", num);
return 0;
}
原理是 C++ 为了保持对 C 语言的兼容性,对 C 语言库进行了重新包装,以符合 C++ 的命名空间等规则。
二、命名空间
1. 作用
防止多人项目代码中同名冲突。例如,不同团队开发的代码中可能都有一个叫 print
的函数,通过命名空间可以区分:
namespace TeamA {
void print() {
std::cout << "This is from TeamA" << std::endl;
}
}
namespace TeamB {
void print() {
std::cout << "This is from TeamB" << std::endl;
}
}
2. 创建与使用
- 创建:使用
namespace
关键字 + 命名空间名,如namespace MyNamespace { /* 代码 */ }
。 - 使用:
- 大范围全部使用:
using namespace 命名空间名
。 - 大范围指定使用:
using 命名空间名::标识符
。 - 小范围指定:
命名空间名::项
。
- 大范围全部使用:
int main() {
TeamA::print(); // 明确指定调用TeamA命名空间的print函数
using TeamB::print; // 引入TeamB命名空间的print函数
print();
return 0;
}
3. 作用域运算符
作用域运算符 ::
用于解决命名冲突,当不同命名空间或全局作用域与局部作用域有同名变量或函数时,通过 ::
可以明确指定使用哪个作用域的实体。例如:
int globalNum = 10;
int main() {
int globalNum = 20;
std::cout << ::globalNum << std::endl; // 输出全局作用域的globalNum,即10
std::cout << globalNum << std::endl; // 输出局部作用域的globalNum,即20
return 0;
}
4. 重名问题
编译器会将同名的命名空间合并,但内部重名的变量或函数会报错。例如:
namespace Shared {
int value = 5;
}
namespace Shared {
// 这里如果再定义int value会报错
void func() {
std::cout << "In Shared namespace" << std::endl;
}
}
5. 函数定义
如果函数声明在命名空间内,定义可以在外部,但需要指定命名空间。例如:
namespace Utility {
void printMessage();
}
void Utility::printMessage() {
std::cout << "This is a message from Utility namespace" << std::endl;
}
int main() {
Utility::printMessage();
return 0;
}
三、标准输入输出
1. 标准输入
使用 std::cin >> 变量
进行输入。例如:
int num;
std::cout << "Enter a number: ";
std::cin >> num;
std::cout << "You entered: " << num << std::endl;
原理是 cin
是 istream
类的对象,>>
运算符被重载用于从标准输入流(键盘)读取数据并存储到指定变量中。
2. 标准输出
使用 std::cout << 内容 << std::endl
进行输出,std::endl
会输出换行并刷新缓冲区。例如:
std::cout << "Hello, World!" << std::endl;
cout
是 ostream
类的对象,<<
运算符被重载用于将数据输出到标准输出流(屏幕)。
四、引用
1. 类型
数据类型 & 变量名(如 int& ref
),还存在被引用变量的情况。
2. 作用
给变量取别名,与变量共享内存。例如:
int num = 10;
int& ref = num;
ref = 20; // 此时num也变为20
std::cout << "num: " << num << ", ref: " << ref << std::endl;
3. 特点
- 常量不能被普通引用绑定,但常量引用可绑定常量和变量。
- 必须初始化,如
int& ref;
是错误的,应int num; int& ref = num;
。 - 无法更换更改为对象,一旦引用绑定到某个变量,就不能再指向其他变量。
- 引用和变量类型需相同。
4. 练习示例
可编写函数利用引用交换两个整数的值:
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 5, y = 10;
swap(x, y);
std::cout << "x: " << x << ", y: " << y << std::endl;
return 0;
}
五、其他数据类型
1. bool 类型
占 1 字节,true
对应非零值(一般为 1),false
对应 0 值。例如:
bool flag = true;
if (flag) {
std::cout << "The flag is true" << std::endl;
}
2. string 类型
可进行直接操作,如复制、拼接、比较等,可和 const char*
相互转换。例如:
#include <string>
int main() {
std::string str1 = "Hello";
std::string str2 = "World";
std::string result = str1 + " " + str2; // 拼接
std::cout << result << std::endl;
const char* cstr = result.c_str(); // 转换为const char*
std::string str3(cstr); // 从const char*转换回string
return 0;
}
3. auto 类型(C++11 起可用)
能自动推导类型,例如:
auto num = 10; // 自动推导num为int类型
auto str = std::string("Auto type"); // 自动推导str为std::string类型
原理是编译器根据初始化表达式的类型来确定 auto
声明的变量类型。
六、名词解释
1. 类
C++ 中类是对 C 语言结构体的扩展,struct
在 C++ 中也可当作类使用。例如:
class Rectangle {
private:
int width;
int height;
public:
Rectangle(int w, int h) : width(w), height(h) {}
int area() {
return width * height;
}
};
2. 对象
类产生的变量,类中可包含结构体变量。例如:
Rectangle rect(5, 3); // 创建Rectangle类的对象rect
std::cout << "Area: " << rect.area() << std::endl;
3. struct 区别
C++ 中的 struct
可存放函数和变量,与 class
主要区别在于默认访问权限,struct
默认成员为 public
,class
默认成员为 private
。例如:
struct Point {
int x;
int y;
void print() {
std::cout << "(" << x << ", " << y << ")" << std::endl;
}
};
class Circle {
int radius;
public:
Circle(int r) : radius(r) {}
int getRadius() {
return radius;
}
};
4. 类种类
常见的有 struct
、union
、class
,一般常用 struct
(多用于简单数据结构)和 class
(用于更复杂的面向对象设计)。
5. 访问权限
public
:成员公开,类内外都可访问。private
:成员私有,只有类内成员函数可访问。protected
:受保护,类内和派生类内可访问。例如:
class Base {
private:
int privateData;
protected:
int protectedData;
public:
int publicData;
};
class Derived : public Base {
public:
void accessData() {
// 不能访问privateData
// 可以访问protectedData
protectedData = 10;
publicData = 20;
}
};
6. 数据分类规则
成员属性一般设为私有,通过公开的成员方法(如 getter
和 setter
函数)来访问和修改,以实现数据封装和保护。例如:
class Student {
private:
std::string name;
int age;
public:
Student(std::string n, int a) : name(n), age(a) {}
std::string getName() const {
return name;
}
int getAge() const {
return age;
}
};
7. 内外访问取向
访问成员位置是否在类作用域内判断访问权限是否允许。在类外只能访问 public
成员,在类内可访问所有权限成员。
七、struct 和 class 区别
除了默认访问权限不同(struct
默认 public
,class
默认 private
),在继承等特性上,语法使用基本一致。例如:
struct BaseStruct {
int baseData;
};
class DerivedClass : public BaseStruct {
public:
void useBaseData() {
baseData = 10;
}
};
这里 DerivedClass
以 public
方式继承 BaseStruct
,可以访问 BaseStruct
的 public
成员 baseData
。
八、类的初始化
1. 分支主题 - 构造函数及相关知识
- 特点:创建对象时自动调用,与类名相同,无返回值类型,可重载,形参列表表明类型不同。例如:
class Complex {
private:
double real;
double imag;
public:
Complex() : real(0), imag(0) {} // 默认构造函数
Complex(double r, double i) : real(r), imag(i) {} // 带参数构造函数
void print() {
std::cout << real << " + " << imag << "i" << std::endl;
}
};
- 调用方式:
- 多种显式调用方式,如
Complex c1(1, 2);
(直接初始化),Complex c2 = Complex(3, 4);
(复制初始化)。 - 隐式调用(部分拆分),如
Complex c3 = {5, 6};
。 - 可用
explicit
关键字禁止隐式转换构造,如explicit Complex(double r) : real(r), imag(0) {}
,此时Complex c = 7;
这种隐式转换构造就会报错。
- 多种显式调用方式,如
- 接口:类内提供外部修改及访问成员数据的函数,常为
getter
(获取数据,如const
成员函数保证不修改数据)和setter
函数(设置数据)。例如:
class Person {
private:
std::string name;
public:
Person(std::string n) : name(n) {}
std::string getName() const {
return name;
}
void setName(std::string n) {
name = n;
}
};
九、C++ 中的 const
1. 函数传参尽量用 const 修饰
可以防止函数内部意外修改传入的参数。例如:
void printString(const std::string& str) {
// 这里不能修改str
std::cout << str << std::endl;
}
2. 普通指针和 const 指针转换规则严格
- 指向常量的指针:
const int* ptr;
,不能通过该指针修改所指的值,但指针本身可以指向其他地址。例如:
const int num = 10;
const int* ptr = #
// *ptr = 20; 错误,不能通过ptr修改num的值
int anotherNum = 20;
ptr = &anotherNum; // 正确,指针可以指向其他地址
- 常量指针:
int* const ptr;
,指针本身不能再指向其他地址,但可以通过指针修改所指的值(如果值本身不是常量)。例如:
int num = 10;
int* const ptr = #
*ptr = 20; // 正确,可以修改所指的值
// ptr = &anotherNum; 错误,指针不能再指向其他地址
十、this 指针
对象调用成员方法时,其地址作为隐藏参数传入,类成员函数内指向当前对象地址。this
指针是常量指针,指向不可变,但可通过在函数声明与定义间加 const
限定其指向的对象为常量。例如:
class Counter {
private:
int count;
public:
Counter() : count(0) {}
void increment() {
this->count++; // 通过this指针访问成员变量
}
int getCount() const {
return count;
}
};
在 increment
函数中,this
指针指向调用该函数的 Counter
对象,从而可以访问和修改对象的成员变量 count
。在 getCount
函数中,由于函数声明为 const
,this
指针指向的对象被视为常量,不能通过 this
指针修改对象成员。
十一、列表初始化
用于初始化类成员属性,构造函数编译器先执行列表初始化部分,再执行用户自定义执行部分。例如:
class Point {
private:
int x;
int y;
public:
Point(int a, int b) : x(a), y(b) {
// 这里是用户自定义执行部分,列表初始化先完成x和y的初始化
}
void print() {
std::cout << "(" << x << ", " << y << ")" << std::endl;
}
};
在创建 Point
对象时,先通过列表初始化对 x
和 y
进行赋值,然后再执行构造函数体内的其他代码(如果有)。