C++编程
字数 1621 2025-08-22 12:23:41
C++面向对象编程与文件操作全面教程
一、类和对象基础
1. 静态成员
静态成员分为静态成员变量和静态成员函数:
class Person {
public:
// 静态成员函数
static void func() {
m_A = 100; // 可以访问静态成员变量
// m_B = 200; // 错误:不能访问非静态成员变量
cout << "static func调用" << endl;
}
static int m_A; // 静态成员变量
int m_B; // 非静态成员变量
private:
static void func2() { // 私有静态成员函数
cout << "static void func2调用" << endl;
}
};
int Person::m_A = 0; // 静态成员变量类外初始化
void test() {
Person p;
p.func(); // 通过对象访问
Person::func(); // 通过类名访问
// Person::func2(); // 错误:私有成员不能访问
}
关键点:
- 静态成员变量属于类,所有对象共享同一份数据
- 静态成员函数只能访问静态成员,不能访问非静态成员
- 静态成员函数也有访问权限(public/private/protected)
- 静态成员变量必须在类外初始化
2. 成员存储方式
class Person {
public:
int m_a; // 非静态成员变量 - 属于对象
static int m_b; // 静态成员变量 - 不属于对象
void func(){} // 非静态成员函数 - 不属于对象
static void func2(){} // 静态成员函数 - 不属于对象
};
void test() {
Person p;
cout << "size of p=" << sizeof(p) << endl; // 输出4(只有m_a的大小)
}
关键点:
- 只有非静态成员变量属于类的对象,占用对象空间
- 空对象占用1字节,用于标识内存位置
- 成员函数(静态/非静态)都不占用对象空间
二、this指针
1. this指针基本用法
class Person {
public:
Person(int age) {
this->age = age; // 解决命名冲突
}
Person& addAge(Person& p) {
this->age += p.age;
return *this; // 返回对象本身
}
int age;
};
void test() {
Person p1(10);
Person p2(10);
p2.addAge(p1).addAge(p1); // 链式编程
cout << "p2的年龄为:" << p2.age << endl; // 输出30
}
关键点:
- this指针指向当前对象
- 用途1:解决形参和成员变量同名问题
- 用途2:实现链式编程(返回对象本身)
2. 空指针访问成员函数
class Person {
public:
void showClassName() {
cout << "this is person class" << endl;
}
void showPersonAge() {
if (this == NULL) return; // 防止空指针访问
cout << "age=" << m_Age << endl;
}
int m_Age;
};
void test() {
Person* p = NULL;
p->showClassName(); // 可以调用
p->showPersonAge(); // 需要检查this指针
}
关键点:
- 空指针可以调用成员函数
- 如果函数中使用this指针,需要检查是否为NULL
三、const修饰成员
1. 常函数与常对象
class Person {
public:
void showPerson() const { // 常函数
// m_A = 100; // 错误:不能修改成员变量
this->m_B = 100; // mutable成员可以修改
// this = NULL; // 错误:不能修改this指向
}
void func() {}
int m_A;
mutable int m_B; // 特殊变量,常函数中可修改
};
void test() {
const Person p; // 常对象
// p.m_A = 100; // 错误:不能修改
p.m_B = 100; // mutable成员可以修改
p.showPerson(); // 可以调用常函数
// p.func(); // 错误:不能调用非常函数
}
关键点:
- 常函数:成员函数后加const,不能修改成员变量(mutable除外)
- 常对象:声明前加const,只能调用常函数
- mutable成员在常函数和常对象中都可修改
四、拷贝构造
1. 深浅拷贝问题
class Person {
public:
Person(int age, int high) {
m_age = age;
m_High = new int(high); // 堆区分配内存
}
// 深拷贝
Person(const Person& p) {
m_age = p.m_age;
m_High = new int(*p.m_High); // 重新分配内存
}
~Person() {
if (m_High != NULL) {
delete m_High; // 释放堆区内存
m_High = NULL;
}
}
int m_age;
int* m_High; // 指针成员
};
void test() {
Person p1(18, 160);
Person p2(p1); // 调用拷贝构造
}
关键点:
- 浅拷贝:简单值拷贝,会导致重复释放堆内存
- 深拷贝:重新分配堆内存,解决重复释放问题
- 有指针成员时必须实现深拷贝
五、运算符重载
1. 基本运算符重载
class Person {
public:
// 成员函数重载+
Person operator+(Person &p) {
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}
int m_A;
int m_B;
};
// 全局函数重载+
Person operator+(Person& p1, Person& p2) {
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
2. 左移运算符重载
class Person {
friend ostream& operator<<(ostream& cout, Person& p);
public:
Person(int a, int b) : m_A(a), m_B(b) {}
private:
int m_A;
int m_B;
};
ostream& operator<<(ostream& cout, Person& p) {
cout << "m_A=" << p.m_A << " m_B=" << p.m_B;
return cout;
}
3. 递增运算符重载
class MyInteger {
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
MyInteger() { m_Num = 0; }
// 前置++
MyInteger& operator++() {
m_Num++;
return *this;
}
// 后置++
MyInteger operator++(int) {
MyInteger temp = *this;
m_Num++;
return temp;
}
private:
int m_Num;
};
4. 赋值运算符重载
class Person {
public:
Person(int age) {
m_Age = new int(age);
}
~Person() {
if (m_Age != NULL) {
delete m_Age;
m_Age = NULL;
}
}
Person& operator=(Person& p) {
if (m_Age != NULL) {
delete m_Age;
m_Age = NULL;
}
m_Age = new int(*p.m_Age); // 深拷贝
return *this;
}
int* m_Age;
};
5. 关系运算符重载
class Person {
public:
Person(string name, int age) : m_Name(name), m_Age(age) {}
bool operator==(Person& p) {
return this->m_Name == p.m_Name && this->m_Age == p.m_Age;
}
string m_Name;
int m_Age;
};
6. 函数调用运算符重载(仿函数)
class MyPrint {
public:
void operator()(string test) {
cout << test << endl;
}
};
class MyAdd {
public:
int operator()(int num1, int num2) {
return num1 + num2;
}
};
void test() {
MyPrint myprint;
myprint("hello world"); // 仿函数调用
MyAdd add;
int ret = add(100, 100);
cout << MyAdd()(100, 100); // 匿名函数对象
}
关键点:
- 运算符重载使自定义类型可以像内置类型一样操作
- =、[]、()、-> 必须重载为成员函数
- <<、>> 通常重载为全局函数
- 前置++返回引用,后置++返回值
- 有指针成员时必须重载赋值运算符实现深拷贝
六、继承
1. 基本语法
// 基类
class BasePage {
public:
void header() { cout << "公共头部" << endl; }
void footer() { cout << "公共底部" << endl; }
void left() { cout << "公共左侧" << endl; }
};
// 派生类
class Java : public BasePage {
public:
void content() { cout << "Java内容" << endl; }
};
class Python : public BasePage {
public:
void content() { cout << "Python内容" << endl; }
};
关键点:
- 减少代码重复
- class 子类 : 继承方式 父类
- 子类(派生类)拥有父类(基类)的成员
2. 继承方式
| 继承方式 | 基类public成员 | 基类protected成员 | 基类private成员 |
|---|---|---|---|
| public | public | protected | 不可访问 |
| protected | protected | protected | 不可访问 |
| private | private | private | 不可访问 |
class Base {
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 : public Base {
// m_A public
// m_B protected
// m_C 不可访问
};
class Son2 : protected Base {
// m_A protected
// m_B protected
// m_C 不可访问
};
class Son3 : private Base {
// m_A private
// m_B private
// m_C 不可访问
};
3. 继承中的构造析构顺序
class Base {
public:
Base() { cout << "Base构造" << endl; }
~Base() { cout << "Base析构" << endl; }
};
class Son : public Base {
public:
Son() { cout << "Son构造" << endl; }
~Son() { cout << "Son析构" << endl; }
};
void test() {
Son s;
// 输出顺序:
// Base构造
// Son构造
// Son析构
// Base析构
}
关键点:
- 先构造父类,再构造子类
- 析构顺序与构造相反
4. 同名成员处理
class Base {
public:
int m_A = 100;
void func() { cout << "Base func" << endl; }
};
class Son : public Base {
public:
int m_A = 200;
void func() { cout << "Son func" << endl; }
};
void test() {
Son s;
cout << s.m_A << endl; // 200
cout << s.Base::m_A << endl; // 100
s.func(); // Son func
s.Base::func(); // Base func
}
5. 多继承
class Base1 {
public:
int m_A = 100;
};
class Base2 {
public:
int m_A = 200;
};
class Son : public Base1, public Base2 {
public:
int m_C = 300;
};
void test() {
Son s;
cout << s.Base1::m_A << endl; // 100
cout << s.Base2::m_A << endl; // 200
}
6. 菱形继承与虚继承
class Animal {
public:
int m_Age;
};
class Sheep : virtual public Animal {};
class Tuo : virtual public Animal {};
class SheepTuo : public Sheep, public Tuo {};
void test() {
SheepTuo st;
st.Sheep::m_Age = 18;
st.Tuo::m_Age = 28;
cout << st.m_Age << endl; // 28 (只有一份数据)
}
关键点:
- 菱形继承会导致数据冗余和二义性
- 虚继承(virtual)解决菱形继承问题
- 虚基类指针(vbptr)指向虚基类表(vbtable)
七、多态
1. 基本语法
class Animal {
public:
virtual void speak() { // 虚函数
cout << "动物在说话" << endl;
}
};
class Cat : public Animal {
public:
void speak() { // 重写虚函数
cout << "小猫在说话" << endl;
}
};
void doSpeak(Animal& animal) {
animal.speak(); // 动态绑定
}
void test() {
Cat cat;
doSpeak(cat); // 输出"小猫在说话"
}
关键点:
- 动态多态条件:
- 有继承关系
- 子类重写父类虚函数
- 使用:父类指针或引用指向子类对象
2. 纯虚函数和抽象类
class Base {
public:
virtual void func() = 0; // 纯虚函数
};
class Son : public Base {
public:
virtual void func() {
cout << "func调用" << endl;
}
};
void test() {
// Base b; // 错误:抽象类不能实例化
Base* base = new Son;
base->func();
}
关键点:
- 纯虚函数语法:virtual 返回类型 函数名(参数)=0;
- 有纯虚函数的类是抽象类,不能实例化
- 子类必须重写所有纯虚函数,否则也是抽象类
3. 虚析构和纯虚析构
class Animal {
public:
Animal() { cout << "Animal构造" << endl; }
virtual ~Animal() = 0; // 纯虚析构
};
Animal::~Animal() { cout << "Animal纯虚析构" << endl; }
class Cat : public Animal {
public:
Cat(string name) {
m_Name = new string(name);
}
~Cat() {
if (m_Name != NULL) {
delete m_Name;
m_Name = NULL;
}
}
string* m_Name;
};
void test() {
Animal* animal = new Cat("Tom");
delete animal; // 正确调用子类析构
}
关键点:
- 解决父类指针释放子类对象问题
- 纯虚析构需要类外实现
- 有纯虚析构的类也是抽象类
八、文件操作
1. 文本文件操作
写文件:
void test() {
ofstream ofs;
ofs.open("test.txt", ios::out);
ofs << "姓名:张三" << endl;
ofs << "年龄:18" << endl;
ofs.close();
}
读文件:
void test() {
ifstream ifs;
ifs.open("test.txt", ios::in);
if (!ifs.is_open()) return;
// 四种读取方式:
// 1. 按单词读取
char buf[1024];
while (ifs >> buf) { cout << buf << endl; }
// 2. 按行读取
while (ifs.getline(buf, sizeof(buf))) { cout << buf << endl; }
// 3. string读取
string str;
while (getline(ifs, str)) { cout << str << endl; }
// 4. 按字符读取
char c;
while ((c = ifs.get()) != EOF) { cout << c; }
ifs.close();
}
2. 二进制文件操作
写文件:
class Person {
public:
char m_Name[64];
int m_Age;
};
void test() {
ofstream ofs("person.bin", ios::out | ios::binary);
Person p = {"张三", 18};
ofs.write((const char*)&p, sizeof(Person));
ofs.close();
}
读文件:
void test() {
ifstream ifs("person.bin", ios::in | ios::binary);
if (!ifs.is_open()) return;
Person p;
ifs.read((char*)&p, sizeof(Person));
cout << p.m_Name << " " << p.m_Age << endl;
ifs.close();
}
关键点:
- 文本文件:ASCII格式,可用<<和>>操作
- 二进制文件:直接内存拷贝,效率高
- 打开模式:ios::in/out/binary/app(追加)/ate(初始位置结尾)
- 二进制读写用read/write函数