C++ 类和对象 详细总结

1. 从结构体到类

详细概念

  • 在 C++ 中,structclass 都用于定义数据结构。struct 主要用于表示简单数据结构,而 class 则用于封装复杂的数据和行为。
  • structclass 之间的主要区别在于默认访问权限:struct 的成员默认是 public,而 class 的成员默认是 private
  • 使用 struct 可以更方便地实现简单的数据结构,而 class 提供了更多的封装、继承和多态特性。
  • 在 C++ 中,可以使用 typedefusing 来简化类型定义。
  • #include <iostream>

    struct MyStruct {
        int x; // 默认 public
    };

    class MyClass {
    private: // 默认 private
        int x;

    public:
        MyClass(int val) : x(val) {} // 构造函数

        int getX() const { return x; } // 访问器函数
    };

    int main() {
        MyStruct s;
        s.x = 10; // 直接访问 public 成员
        std::cout << "MyStruct x: " << s.x << std::endl;

        MyClass c(20); // 创建 MyClass 对象
        std::cout << "MyClass x: " << c.getX() << std::endl; // 通过访问器函数获取值

        return 0;
    }

  • 概念补充structclass 的使用场景不同:struct 通常用于简单数据存储,而 class 更适合复杂的数据结构和行为。struct 适合轻量级对象,不需要复杂逻辑时使用。

  • 对比struct:默认 public,适合简单数据。class:默认 private,适合复杂逻辑和封装。
  • 实际应用:使用 struct 设计数据传输对象(DTO),如网络传输中的 JSON 数据映射。使用 class 设计业务逻辑层对象,管理复杂的行为和状态。

2. 类的访问权限

C++ 中类的访问权限有三种:

  • public:公开访问
  • protected:保护访问,仅限于该类及其派生类
  • private:私有访问,仅限于该类内部
  • 访问权限控制了外部代码对类成员的访问。正确使用访问权限有助于实现封装,保护数据不被外部代码随意修改。
  • 实际应用:在设计 API 时,将重要的实现细节设为 private,公开接口方法为 public。设计基类时,保护成员变量以避免直接修改。

  • #include <iostream>

    class MyClass {
    public:
        int publicVar; // 任何地方可访问

    protected:
        int protectedVar; // 仅限派生类访问

    private:
        int privateVar; // 仅限本类访问

    public:
        MyClass(int pub, int prot, int priv) 
            : publicVar(pub), protectedVar(prot), privateVar(priv) {}

        void display() {
            std::cout << "Public: " << publicVar << ", Protected: " << protectedVar 
                      << ", Private: " << privateVar << std::endl;
        }
    };

    class Derived : public MyClass {
    public:
        Derived(int pub, int prot, int priv) : MyClass(pub, prot, priv) {}

        void show() {
            std::cout << "Derived Public: " << publicVar << ", Protected: " << protectedVar << std::endl;
            // std::cout << "Private: " << privateVar; // 错误,无法访问私有成员
        }
    };

    int main() {
        MyClass obj(1, 2, 3);
        obj.display(); // 访问公共和保护成员

        Derived d(4, 5, 6);
        d.show(); // 访问派生类中的公共和保护成员

        return 0;
    }

  • 补充知识点

  • 访问权限的正确使用能提升代码的安全性和可维护性。
  • 可以使用 friend 关键字让特定函数或类访问私有成员。

3. 简单使用类

类结合数据和行为,允许对象通过成员函数进行数据操作。成员函数可以修改对象的状态,并提供对外接口

#include <iostream>

class Rectangle {
private:
    int width, height; // 封装数据

public:
    // 构造函数
    Rectangle(int w, int h) : width(w), height(h) {}

    // 成员函数:计算面积
    int area() const { // const 修饰,表示该函数不会修改对象
        return width * height;
    }

    // 设置尺寸
    void setDimensions(int w, int h) {
        width = w;
        height = h;
    }

    void display() const { // 显示尺寸
        std::cout << "Width: " << width << ", Height: " << height << std::endl;
    }
};

int main() {
    Rectangle rect(5, 10); // 创建对象并初始化
    rect.display(); // 输出 Width: 5, Height: 10
    std::cout << "Area: " << rect.area() << std::endl; // 输出面积
    return 0;
}

补充知识点

  • 通过成员函数实现封装,外部代码无法直接访问对象的私有数据。
  • 使用 const 关键字可以防止成员函数修改对象状态,增加代码的可读性和安全性。

4. 构造函数与析构函数

详细概念

  • 构造函数用于初始化对象,可以重载以接受不同数量和类型的参数。
  • 析构函数用于清理资源,比如释放动态分配的内存。它没有参数和返回值。

代码示例

#include <iostream>

class MyClass {
public:
    MyClass() { // 默认构造函数
        std::cout << "Default Constructor called!" << std::endl;
    }

    MyClass(int value) { // 带参数的构造函数
        std::cout << "Parameterized Constructor called with value: " << value << std::endl;
    }

    ~MyClass() { // 析构函数
        std::cout << "Destructor called!" << std::endl;
    }
};

int main() {
    MyClass obj1; // 调用默认构造函数
    MyClass obj2(10); // 调用带参数的构造函数
    // obj1 和 obj2 的生命周期结束时调用析构函数
    return 0;
}

补充知识点

  • 如果没有定义构造函数,C++ 会生成一个默认构造函数。
  • 如果类中有指针成员,建议在析构函数中释放动态分配的内存,以防内存泄漏。

5. 构造函数的细节

详细概念

  • 初始化列表是一种更高效的初始化方式,特别适用于 const 和引用类型的成员。
  • 可以在同一个类中定义多个构造函数(重载)。

代码示例

#include <iostream>

class Point {
public:
    int x, y;

    // 默认构造函数
    Point() : x(0), y(0) {
        std::cout << "Default Point created at (0, 0)" << std::endl;
    }

    // 带参数的构造函数
    Point(int xVal, int yVal) : x(xVal), y(yVal) {
        std::cout << "Point created at (" << x << ", " << y << ")" << std::endl;
    }

    void display() const {
        std::cout << "Point: (" << x << ", " << y << ")" << std::endl;
    }
};

int main() {
    Point p1; // 调用默认构造函数
    Point p2(10, 20); // 调用带参数的构造函数

    p1.display(); // 输出 Point: (0, 0)
    p2.display(); // 输出 Point: (10, 20)
    return 0;
}

6. 拷贝构造函数

详细概念

  • 拷贝构造函数用于创建对象的副本,默认情况下执行浅拷贝。需要手动实现深拷贝以防止内存泄漏或数据冲突。

代码示例

#include <iostream>

class MyClass {
public:
    int* data; // 动态分配内存

    MyClass(int value) {
        data = new int(value); // 分配内存
        std::cout << "Constructor called, data: " << *data << std::endl;
    }

    // 拷贝构造函数
    MyClass(const MyClass& other) {
        data = new int(*other.data); // 深拷贝
        std::cout << "Copy constructor called, data: " << *data << std::endl;
    }

    ~MyClass() {
        delete data; // 释放内存
        std::cout << "Destructor called!" << std::endl;
    }
};

int main() {
    MyClass obj1(42); // 创建对象
    MyClass obj2 = obj1; // 使用拷贝构造函数创建副本
    std::cout << "obj2 data: " << *obj2.data << std::endl; // 输出 42
    return 0;
}
 

7.深拷贝与浅拷贝

详细概念

  • 浅拷贝:简单复制对象的所有字段,包括指针。会导致多个对象共享同一内存。
  • 深拷贝:为每个对象分配新的内存,复制指针指向的数据,确保每个对象拥有独立的资源。

代码示例

#include <iostream>

class Shallow {
public:
    int* data;

    Shallow(int value) {
        data = new int(value); // 动态分配内存
    }

    // 浅拷贝构造函数
    Shallow(const Shallow& other) : data(other.data) {} // 仅复制指针

    ~Shallow() {
        delete data; // 可能导致错误
        std::cout << "Destructor called!" << std::endl;
    }
};

int main() {
    Shallow obj1(42);
    Shallow obj2 = obj1; // 浅拷贝,obj1 和 obj2 共享同一内存
    std::cout << "obj2 data: " << *obj2.data << std::endl; // 输出 42
    return 0; // 当程序结束时,析构函数被调用,会导致 double free 错误
}

补充知识点

  • 浅拷贝可能会导致程序崩溃或未定义行为,尤其是在多个对象共享指向同一内存的情况下。
  • 深拷贝确保每个对象有独立的内存,避免资源冲突,适用于动态分配的资源。

8. 初始化列表

详细概念

  • 初始化列表允许在构造函数中直接初始化成员变量,特别是对于 const 和引用类型成员,必须使用初始化列表。

代码示例

#include <iostream>

class MyClass {
public:
    const int value; // const 成员

    MyClass(int val) : value(val) {} // 使用初始化列表

    void display() const {
        std::cout << "Value: " << value << std::endl;
    }
};

int main() {
    MyClass obj(42);
    obj.display(); // 输出 Value: 42
    return 0;
}

补充知识点

  • 成员按声明顺序初始化,而非初始化列表的顺序。
  • 使用初始化列表可以提高效率,特别是对于复杂对象的构造。

9. const 修饰成员函数

详细概念

  • const 修饰符用于标识一个成员函数不会修改对象的状态。它提高了代码的安全性,允许 const 对象调用这些函数。
  • const 函数可以修改对象,而 const 函数不允许。

代码示例

#include <iostream>

class MyClass {
public:
    int value;

    MyClass(int val) : value(val) {}

    int getValue() const { // const 成员函数
        return value; // 只读
    }

    void setValue(int val) { // 非 const 成员函数
        value = val; // 可以修改
    }
};

int main() {
    MyClass obj(42);
    std::cout << "Value: " << obj.getValue() << std::endl; // 输出 Value: 42
    obj.setValue(100);
    std::cout << "New Value: " << obj.getValue() << std::endl; // 输出 New Value: 100
    return 0;
}

10. this 指针

详细概念

  • this 指针指向当前对象的实例,通常用于区分成员变量和参数名相同的情况。

代码示例

#include <iostream>

class MyClass {
public:
    int value;

    MyClass(int value) {
        this->value = value; // 使用 this 区分
    }

    void show() {
        std::cout << "Value: " << this->value << std::endl; // 可以省略 this
    }
};

int main() {
    MyClass obj(42);
    obj.show(); // 输出 Value: 42
    return 0;
}

补充知识点

  • this 在类的静态成员函数中不可用,因为静态成员与具体的对象无关。
  • this 指针可以用于链式调用,以便返回对象本身。

11. 类的静态成员

详细概念

  • 静态成员属于类而不是特定的对象。所有对象共享同一静态成员,适合用于类级别的属性或计数。

代码示例

#include <iostream>

class MyClass {
public:
    static int count; // 静态成员

    MyClass() {
        count++;
    }
};

// 静态成员的定义和初始化
int MyClass::count = 0;

int main() {
    MyClass obj1;
    MyClass obj2;
    std::cout << "Count: " << MyClass::count << std::endl; // 输出 Count: 2
    return 0;
}

补充知识点

  • 静态成员可以通过类名直接访问,且与类的实例无关。
  • 静态成员的生命周期与程序相同,直到程序结束时才释放。

12. 简单对象模型

详细概念

  • C++ 支持值语义和引用语义。值语义涉及对象的直接复制,引用语义涉及对象的引用或指针,允许共享和修改同一对象。

代码示例

#include <iostream>

class MyClass {
public:
    int value;

    MyClass(int val) : value(val) {}
};

void byValue(MyClass obj) { // 值语义,复制对象
    obj.value += 10; // 不影响原对象
}

void byReference(MyClass& obj) { // 引用语义,直接修改对象
    obj.value += 10; // 影响原对象
}

int main() {
    MyClass a(10);
    byValue(a); // 调用 byValue,不影响 a
    std::cout << "After byValue: " << a.value << std::endl; // 输出 10

    byReference(a); // 调用 byReference,影响 a
    std::cout << "After byReference: " << a.value << std::endl; // 输出 20

    return 0;
}

补充知识点

  • 值语义适合简单数据类型或小对象,避免内存开销。
  • 引用语义适合大型对象,能显著提高性能和效率。

13. 友元

详细概念

  • 友元函数或类可以访问类的私有和保护成员。友元关系是单向的,不对称。

代码示例

#include <iostream>

class MyClass {
private:
    int value;

public:
    MyClass(int val) : value(val) {}

    friend void showValue(MyClass obj); // 友元函数声明
};

void showValue(MyClass obj) {
    std::cout << "Value: " << obj.value << std::endl; // 访问私有成员
}

int main() {
    MyClass obj(42);
    showValue(obj); // 输出 Value: 42
    return 0;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值