当前位置: 首页 > article >正文

C++ 类(class)

C++ 类(class)

C++中的类(Class)是一种用户定义的数据类型,它封装了数据(成员变量)和操作这些数据的函数(成员函数)。类可以被看作是创建对象的蓝图,它定义了对象的属性和行为。

访问修饰符

在C++中,访问修饰符用于控制类成员(包括变量和函数)的可见性可访问性

主要有三种访问修饰符:publicprotectedprivate

访问修饰符定义特点说明访问级别
public
(公有)
类成员可以被任何代码访问。- 成员对所有用户都是可见的
- 类的公共接口
- 继承时,基类的public成员在派生类中仍然是public
用于类的接口部分,允许外部代码直接访问。类内外均可访问
protected
(受保护)
类成员可以被类本身和其派生类访问。- 成员对类本身和派生类可见
- 继承时,基类的protected成员在派生类中仍然是protected
用于类和派生类之间的接口,保护成员不被类的外部代码访问。类内外及子类均可访问
private
(私有)
类成员只能被类本身访问。- 成员对类本身可见
- 继承时,基类的private成员在派生类中不可见
用于类的内部实现,完全封装,不允许外部代码访问。仅限类内访问
  • publicprotectedprivate是C++中的三个访问修饰符,它们控制着类成员的可见性和可访问性。
  • public成员是类的公共接口,可以被任何代码访问,包括类的实例和外部代码。
  • protected成员是类和其派生类的接口,只能被类本身和其派生类访问。
  • private成员是类的内部实现细节,只能被类本身访问,完全封装,不允许外部代码访问。
  • 在继承中,基类的publicprotected成员会传递给派生类,而private成员则不会传递,派生类无法访问基类的private成员。
  • 访问修饰符是实现封装和数据隐藏的重要机制,有助于保护数据的完整性和安全性。
#include <iostream>
#include <string>

// 声明一个类Person
class Person {
    // 私有成员变量,只能在类的内部访问
private:
    std::string name;

    // 受保护成员变量,可以在类的内部以及派生类中访问
protected:
    int age;

    // 公有成员变量,可以在任何地方访问
public:
    double height;

    // 私有成员函数,只能在类的内部调用
private:
    void privateFunction() {
        std::cout << "Private function called" << std::endl;
    }

    // 受保护成员函数,可以在类的内部以及派生类中调用
protected:
    void protectedFunction() {
        std::cout << "Protected function called" << std::endl;
    }

    // 公有成员函数,可以在任何地方调用
public:
    void publicFunction() {
        std::cout << "Public function called" << std::endl;
    }

    // 构造函数
public:
    Person(const std::string& nm, int ag, double ht) : name(nm), age(ag), height(ht) {}

    // 公有成员函数,用于外部访问私有成员变量
    std::string getName() const {
        return name;
    }

    // 公有成员函数,用于外部调用私有成员函数
    void callPrivateFunction() {
        privateFunction();
    }
};

// 派生类
class Employee : public Person {
public:
    Employee(const std::string& nm, int ag, double ht) : Person(nm, ag, ht) {}

    // 可以访问基类的受保护成员
    void printAge() {
        std::cout << "Age: " << age << std::endl;
    }

    // 可以调用基类的受保护成员函数
    void printProtectedFunction() {
        protectedFunction();
    }
};

int main() {
    // 创建Employee对象
    Employee emp("John Doe", 30, 175.5);

    // 访问公有成员函数和变量
    emp.publicFunction();
    std::cout << "Height: " << emp.height << std::endl;

    // 通过公有接口访问私有成员变量和私有成员函数
    std::cout << "Name: " << emp.getName() << std::endl;
    emp.callPrivateFunction();

    // 访问受保护成员变量和函数(通过派生类)
    emp.printAge();
    emp.printProtectedFunction();

    return 0;
}

Person类有三个访问修饰符定义的成员变量和成员函数,分别展示了privateprotectedpublic访问级别。Employee类继承自Person类,并可以访问Person类的protected成员。

  • private成员变量name和成员函数privateFunction只能在Person类内部访问。
  • protected成员变量age和成员函数protectedFunction可以在Person类及其派生类Employee中访问。
  • public成员变量height和成员函数publicFunction可以在任何地方访问。

main函数中创建了一个Employee对象,并展示了如何访问不同访问级别的成员。通过Employee对象,我们可以访问Person类的公有成员和受保护成员,但不能直接访问私有成员。私有成员只能通过Person类提供的公有接口(如getNamecallPrivateFunction)来访问。

成员变量

在C++中,类的成员变量是定义在类内部的变量,它们代表了类的状态。

变量类型定义作用访问级别初始化线程安全
非静态成员变量类中定义的变量,每个对象实例都有一份拷贝。存储对象的状态信息。public/private/protected默认初始化/在构造函数中初始化/列表初始化
静态成员变量类中定义的静态变量,所有对象实例共享同一份拷贝。存储类级别的共享状态信息。public/private在类外初始化/静态成员初始化器否(需要外部同步)
常量成员变量使用constconstexpr修饰的成员变量。提供类级别的常量值,不可修改。public/private/protected必须在声明时初始化或在成员初始化列表中初始化
互斥锁成员变量类中定义的互斥锁,如std::mutex用于同步对共享资源的访问,保证同一时间只有一个线程可以访问资源。public/private默认构造/在构造函数中显式初始化是(正确使用时)
条件变量类中定义的条件变量,如std::condition_variable用于线程间的协调,使线程在特定条件满足之前挂起。public/private默认构造/在构造函数中显式初始化是(正确使用时)
原子变量使用 C++11 中的原子类型定义的变量,如std::atomic提供无锁的线程安全操作,如原子读写。public/private默认构造/在构造函数中显式初始化
线程局部存储变量使用 thread_local 定义的变量,每个线程有自己的独立拷贝。存储线程特定的数据,避免线程间的数据竞争。public/private默认初始化/在声明时初始化
  • 非静态成员变量:每个对象实例都有自己的拷贝,因此它们不是线程安全的,除非通过外部同步机制来保护。
  • 静态成员变量:所有对象共享同一份拷贝,因此它们也不是线程安全的,除非通过外部同步机制来保护。
  • 常量成员变量:由于它们是不可变的,因此是线程安全的。
  • 互斥锁成员变量:虽然std::mutex本身是线程安全的,但需要正确使用(例如避免死锁)来保证线程安全。
  • 条件变量std::condition_variable是线程安全的,但需要与互斥锁结合使用,并正确管理条件变量的唤醒和等待。
  • 原子变量std::atomic类型提供了基本的线程安全操作,适用于简单的同步需求。
  • 线程局部存储变量thread_local变量是线程安全的,因为每个线程都有自己的独立拷贝。

成员函数

在C++中,类的成员函数是定义在类内部的函数,它们可以访问类的私有和保护成员。

成员函数定义了类的行为。成员函数可以操作类的成员变量,并且可以被声明为publicprotectedprivate,以控制它们的访问级别。

成员函数类型定义作用特点访问级别使用场景示例
构造函数对象创建时调用的函数,用于初始化对象。初始化新创建的对象。- 与类同名
- 没有返回类型
- 可以有参数
- 不能是虚函数
public对象创建时需要初始化成员变量时Date::Date(int y, int m, int d) : year(y), month(m), day(d) {}
析构函数对象销毁时调用的函数,用于清理资源。释放对象占用的资源。- 与类同名,前面加上~
- 没有返回类型
- 不能被声明为constvolatile
public对象销毁时需要释放资源,如动态内存时Date::~Date() { delete[] year; }
拷贝构造函数用于创建一个对象作为另一个对象的副本的函数。创建一个对象作为另一个对象的副本。- 带有一个常量引用参数
- 可以被用于参数传递和返回值优化
public/private需要深拷贝对象时,或编译器生成的浅拷贝不满足需求时Date::Date(const Date& other) : year(other.year) {}
拷贝赋值运算符将一个对象的内容赋值给另一个对象的函数。将一个对象的内容赋值给另一个对象。- 返回类型为引用
- 带有一个常量引用参数
- 需要处理自赋值
public/private需要自定义赋值逻辑,或编译器生成的浅拷贝不满足需求时Date& Date::operator=(const Date& other) { /* ... */ return *this; }
移动构造函数用于创建一个对象,将其作为另一个对象的移动来源的函数。创建一个对象作为另一个对象的移动来源,转移资源所有权。- 带有一个右值引用参数
- C++11引入
public/private需要移动语义,如转移资源所有权,而不是复制资源时Date::Date(Date&& other) : year(std::move(other.year)) {}
移动赋值运算符将一个对象的内容移动赋值给另一个对象的函数。将一个对象的内容移动赋值给另一个对象,转移资源所有权。- 返回类型为引用
- 带有一个右值引用参数
- C++11引入
public/private需要移动语义,如转移资源所有权,而不是复制资源时Date& Date::operator=(Date&& other) { /* ... */ return *this; }
虚函数允许在派生类中被重写的函数。实现多态性,允许派生类提供特定的实现。- 被声明为virtual
- 可以在派生类中被重写为override
public/protected需要基类和派生类之间存在多态行为时virtual void draw() { /* ... */ }
纯虚函数强制派生类必须提供具体实现的虚函数。使包含它的类成为抽象类,强制派生类提供实现。- 被声明为virtual且后面跟= 0
- 使类成为抽象类
public/protected需要定义接口,但由派生类提供具体实现时virtual void pureVirtual() = 0;
常量成员函数不修改任何成员变量的成员函数。提供对对象的只读操作。- 被声明为const
- 可以被常量对象调用
public/protected需要对对象进行只读操作,而不改变对象状态时int getValue() const { return value; }
静态成员函数不依赖于类的特定对象实例的成员函数。提供与类相关但不需要对象实例的操作。- 使用static关键字声明
- 可以通过类名直接调用
public/protected需要定义与类相关,但与特定对象实例无关的函数时static int add(int a, int b) { return a + b; }
友元函数被赋予访问类的私有和保护成员权限的非成员函数。提供对类私有成员的访问。- 使用friend关键字声明
- 可以是非成员函数或另一个类的成员函数
不适用需要非成员函数访问类的私有或保护成员时friend void display(const MyClass&);
内联函数在编译时被插入到每个调用点的函数。提高性能,减少函数调用的开销。- 使用inline关键字声明
- 通常用于小型函数
public/protected对于小型函数,希望编译器优化为内联调用时inline int add(int a, int b) { return a + b; }
  • 成员函数的访问级别(public/protected/private)决定了它们可以在类的哪个范围内被访问。
  • 构造函数和析构函数是特殊的成员函数,它们在对象的生命周期开始和结束时自动被调用。
  • 拷贝构造函数和拷贝赋值运算符用于对象的拷贝操作,移动构造函数和移动赋值运算符用于对象的移动操作。
  • 虚函数和纯虚函数是实现多态性的关键,它们允许基类通过指针或引用调用派生类的函数。
  • 静态成员函数和友元函数是特殊的成员函数,它们不依赖于对象实例,因此没有初始化列表和初始化顺序的概念。

在C++中,如果类没有显式定义某些成员函数,编译器会根据需要自动生成默认成员函数。这些默认成员函数包括:

  1. 默认构造函数(Default Constructor):
    如果类没有定义任何构造函数,编译器会生成一个默认构造函数。这个函数不接受任何参数,并默认初始化成员变量(对于基本数据类型成员变量,通常是零初始化)。

  2. 默认析构函数(Default Destructor):
    如果类没有定义析构函数,编译器会生成一个默认析构函数。这个函数不执行任何操作,除非类有基类或成员对象需要析构。

  3. 默认拷贝构造函数(Default Copy Constructor):
    如果类没有定义拷贝构造函数,并且没有成员函数被声明为constvolatile,没有虚函数,没有mutable成员,没有基类的自定义拷贝构造函数,编译器会生成一个默认拷贝构造函数。这个函数会逐个成员地拷贝对象。

  4. 默认拷贝赋值运算符(Default Copy Assignment Operator):
    如果类没有定义拷贝赋值运算符,并且没有成员函数被声明为constvolatile,没有虚函数,没有mutable成员,没有基类的自定义拷贝赋值运算符,编译器会生成一个默认拷贝赋值运算符。这个运算符会逐个成员地赋值对象。

  5. 默认移动构造函数(Default Move Constructor):
    C++11及以后版本,如果类没有定义移动构造函数,并且没有成员函数被声明为constvolatile,没有虚函数,没有mutable成员,没有基类的自定义移动构造函数,编译器会生成一个默认移动构造函数。这个函数会逐个成员地移动对象。

  6. 默认移动赋值运算符(Default Move Assignment Operator):
    C++11及以后版本,如果类没有定义移动赋值运算符,并且没有成员函数被声明为constvolatile,没有虚函数,没有mutable成员,没有基类的自定义移动赋值运算符,编译器会生成一个默认移动赋值运算符。这个运算符会逐个成员地移动赋值对象。

#include <iostream>

class SimpleClass {
public:
    int x;
    double y;

    // 默认构造函数
    SimpleClass() = default;

    // 默认析构函数
    ~SimpleClass() = default;

    // 默认拷贝构造函数
    SimpleClass(const SimpleClass& other) = default;

    // 默认拷贝赋值运算符
    SimpleClass& operator=(const SimpleClass& other) = default;

    // 默认移动构造函数
    SimpleClass(SimpleClass&& other) = default;

    // 默认移动赋值运算符
    SimpleClass& operator=(SimpleClass&& other) = default;
};

int main() {
    SimpleClass obj1; // 使用默认构造函数
    SimpleClass obj2 = obj1; // 使用默认拷贝构造函数
    SimpleClass obj3(std::move(obj1)); // 使用默认移动构造函数
    obj2 = obj3; // 使用默认拷贝赋值运算符
    obj3 = std::move(obj2); // 使用默认移动赋值运算符

    return 0;
}

SimpleClass显式地声明了所有默认成员函数,并使用default关键字告诉编译器生成默认实现。即使没有显式声明这些函数,编译器也会为这个类自动生成它们。

C++ 中 class 与 struct

classstruct
定义方式使用class关键字定义
class 类名 { ... };
使用struct关键字定义
struct 结构名 { ... };
默认访问权限私有(private)公共(public)
继承方式私有继承(private inheritance)公共继承(public inheritance)
构造函数和析构函数可以有构造函数和析构函数可以有构造函数和析构函数
成员变量和成员函数可以有成员变量和成员函数可以有成员变量和成员函数
成员变量和成员函数的默认访问权限私有(private)公共(public)
运算符重载可以重载运算符可以重载运算符
模板可以作为模板参数可以作为模板参数
异常处理可以使用异常处理可以使用异常处理

在C++中,structclass的区别主要在于默认访问权限和继承方式。除此之外,它们的功能和用法基本相同。

  • class 和 struct 都可以用来定义自定义数据类型。
  • class 的默认访问权限是私有(private),而 struct 的默认访问权限是公共(public)。
  • class 的继承方式是私有继承(private inheritance),而 struct 的继承方式是公共继承(public inheritance)。
  • class 和 struct 都可以有成员变量和成员函数,构造函数和析构函数,运算符重载,模板和异常处理。

选择使用类还是struct

  • 如果需要定义一个具有私有成员变量和成员函数的类型,应该使用类。
  • 如果需要定义一个具有公共成员变量和成员函数的类型,应该使用struct。
  • 如果需要定义一个具有私有继承的类型,应该使用类。
  • 如果需要定义一个具有公共继承的类型,应该使用struct。

http://www.kler.cn/a/414191.html

相关文章:

  • 数据库(总结自小林coding)|事务的四大特性、数据库的事务隔离级别、MySQL的执行引擎、MySQL为什么使用B+树来作索引
  • 重定向操作和不同脚本的互相调用
  • 使用SOAtest进行功能回归测试
  • Momenta java开发面试题及参考答案
  • 【Ubuntu系统开发工具使用技能】使用VS Code的远程Python交互式jupyter notebook窗口与数据可视化案例
  • 个人回顾。
  • (十一)Python3 接口自动化测试,Pytest-Allure报告的使用
  • Python文件夹.idea的作用
  • 计算机网络 | 7.网络安全
  • 学习笔记037——Java中【Synchronized锁】
  • 111页PDF | 企业IT治理体系规划方案(限免下载)
  • 使用开源GCC编译微软WMI相关函数的示例代码
  • 详细讲解MySQL中的默认索引(B+树)
  • python -从文件夹批量提取pdf文章的第n页,并存储起来
  • git的使用(简洁版)
  • C语言中的指针的解读和强大功能
  • 第T9周:Tensorflow实现猫狗识别(2)
  • 【书生浦语实战营-提示词工程实践】基础闯关任务
  • PHP 循环 - For 循环
  • Day51 | 动态规划 :区间DP 最长回文子序列多边形三角部分的最低得分