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); // 输出"小猫在说话"
}

关键点:

  • 动态多态条件:
    1. 有继承关系
    2. 子类重写父类虚函数
  • 使用:父类指针或引用指向子类对象

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函数
C++面向对象编程与文件操作全面教程 一、类和对象基础 1. 静态成员 静态成员分为静态成员变量和静态成员函数: 关键点: 静态成员变量属于类,所有对象共享同一份数据 静态成员函数只能访问静态成员,不能访问非静态成员 静态成员函数也有访问权限(public/private/protected) 静态成员变量必须在类外初始化 2. 成员存储方式 关键点: 只有非静态成员变量属于类的对象,占用对象空间 空对象占用1字节,用于标识内存位置 成员函数(静态/非静态)都不占用对象空间 二、this指针 1. this指针基本用法 关键点: this指针指向当前对象 用途1:解决形参和成员变量同名问题 用途2:实现链式编程(返回对象本身) 2. 空指针访问成员函数 关键点: 空指针可以调用成员函数 如果函数中使用this指针,需要检查是否为NULL 三、const修饰成员 1. 常函数与常对象 关键点: 常函数:成员函数后加const,不能修改成员变量(mutable除外) 常对象:声明前加const,只能调用常函数 mutable成员在常函数和常对象中都可修改 四、拷贝构造 1. 深浅拷贝问题 关键点: 浅拷贝:简单值拷贝,会导致重复释放堆内存 深拷贝:重新分配堆内存,解决重复释放问题 有指针成员时必须实现深拷贝 五、运算符重载 1. 基本运算符重载 2. 左移运算符重载 3. 递增运算符重载 4. 赋值运算符重载 5. 关系运算符重载 6. 函数调用运算符重载(仿函数) 关键点: 运算符重载使自定义类型可以像内置类型一样操作 =、[ ]、()、-> 必须重载为成员函数 < <、>> 通常重载为全局函数 前置++返回引用,后置++返回值 有指针成员时必须重载赋值运算符实现深拷贝 六、继承 1. 基本语法 关键点: 减少代码重复 class 子类 : 继承方式 父类 子类(派生类)拥有父类(基类)的成员 2. 继承方式 | 继承方式 | 基类public成员 | 基类protected成员 | 基类private成员 | |---------|---------------|-------------------|----------------| | public | public | protected | 不可访问 | | protected | protected | protected | 不可访问 | | private | private | private | 不可访问 | 3. 继承中的构造析构顺序 关键点: 先构造父类,再构造子类 析构顺序与构造相反 4. 同名成员处理 5. 多继承 6. 菱形继承与虚继承 关键点: 菱形继承会导致数据冗余和二义性 虚继承(virtual)解决菱形继承问题 虚基类指针(vbptr)指向虚基类表(vbtable) 七、多态 1. 基本语法 关键点: 动态多态条件: 有继承关系 子类重写父类虚函数 使用:父类指针或引用指向子类对象 2. 纯虚函数和抽象类 关键点: 纯虚函数语法:virtual 返回类型 函数名(参数)=0; 有纯虚函数的类是抽象类,不能实例化 子类必须重写所有纯虚函数,否则也是抽象类 3. 虚析构和纯虚析构 关键点: 解决父类指针释放子类对象问题 纯虚析构需要类外实现 有纯虚析构的类也是抽象类 八、文件操作 1. 文本文件操作 写文件: 读文件: 2. 二进制文件操作 写文件: 读文件: 关键点: 文本文件:ASCII格式,可用< <和>>操作 二进制文件:直接内存拷贝,效率高 打开模式:ios::in/out/binary/app(追加)/ate(初始位置结尾) 二进制读写用read/write函数