欢迎光临
我们一直在努力

C++类和对象(上)

目录

1. 类的定义

  1.1 类定义格式

1.1.1 示例代码(class 定义类)

1.1.2 示例代码(struct 定义类)

  1.2 访问限定符

  1.3 类域

2. 类的实例化

  2.1 实例化概念

  2.2 对象大小计算

  2.2.1 对象存储规则

  2.2.2 内存对齐规则

  2.2.3 示例代码与计算

3. this 指针

  3.1 核心作用

  3.2 特性

  3.3 示例代码

3.4 经典例题(this 指针相关)

例题 1

例题 2

例题 3

4. C++ 与 C 语言实现 Stack 对比(封装特性体现)

  4.1 核心差异(面向对象封装特性)

  4.2 C 语言实现 Stack 代码

  4.3 C++ 实现 Stack 代码

5. 总结(类和对象・上核心)


1. 类的定义

  1.1 类定义格式

   关键字:用class类定义,类名自定义(如Stack),{}内为类的主体,类定义结束必须加分号

   类的成员:

  • 成员变量(属性):类中的变量,习惯加特殊标识(如前缀_、前缀m_、后缀_,如_year、m_capacity、top_),非强制,按公司规范执行。
  • 成员函数(方法):类中的函数,定义在类内的成员函数默认为inline(内联函数)
  •  struct与class的关系:

  • C++ 中struct可定义类,兼容 C 中struct用法,同时支持定义成员函数。
  • 区别:class默认访问权限为private,struct默认访问权限为public;推荐用class定义类。
  •     类型简化:

        C++ 中struct定义的 类无需typedef,类名可直接作为类型(如struct ListNodeCPP可直接声明ListNodeCPP obj,无需typedef)。

    1.1.1 示例代码(class 定义类)

    #include<iostream>
    using namespace std;

    class Stack
    {
    public:
    // 成员函数
    void Init(int n = 4)
    {
    array = (int*)malloc(sizeof(int) * n);
    if (nullptr == array)
    {
    perror("malloc申请空间失败");
    return;
    }
    capacity = n;
    top = 0;
    }

    void Push(int x)
    {
    // 扩容逻辑(省略)
    array[top++] = x;
    }

    int Top()
    {
    assert(top > 0);
    return array[top – 1];
    }

    void Destroy()
    {
    free(array);
    array = nullptr;
    top = capacity = 0;
    }

    private:
    // 成员变量(加_前缀)
    int* array;
    size_t capacity;
    size_t top;
    }; // 分号不能省略

    int main()
    {
    Stack st;
    st.Init();
    st.Push(1);
    st.Push(2);
    cout << st.Top() << endl;
    st.Destroy();
    return 0;
    }

    1.1.2 示例代码(struct 定义类)

    #include<iostream>
    using namespace std;

    // C风格struct(需typedef)
    typedef struct ListNodeC
    {
    struct ListNodeC* next;
    int val;
    }LTNode;

    // C++风格struct(无需typedef,可定义成员函数)
    struct ListNodeCPP
    {
    void Init(int x)
    {
    next = nullptr;
    val = x;
    }
    ListNodeCPP* next;
    int val;
    };

    int main()
    {
    ListNodeCPP node;
    node.Init(10);
    return 0;
    }

      1.2 访问限定符

    • 核心作用:实现封装,选择性向外部暴露接口,限制成员的访问权限。

    • 三种访问限定符及规则:
      访问限定符类外访问权限作用域范围
      public 可直接访问 从声明位置到下一个访问限定符或类结束
      protected 不可直接访问 从声明位置到下一个访问限定符或类结束
      private 不可直接访问 从声明位置到下一个访问限定符或类结束
    • 注意:protected和private在类外访问权限一致,仅在继承中体现区别;成员变量通常设为private/protected,对外提供的成员函数设为public。

      1.3 类域

    • 定义:类定义一个独立的作用域,所有成员均在该作用域内。
    • 类外定义成员函数:需用::作用域操作符指明类域,否则编译器会视为全局函数。
    • 示例代码: #include<iostream>
      using namespace std;

      class Stack
      {
      public:
      // 类内声明成员函数
      void Init(int n = 4);
      private:
      int* array;
      size_t capacity;
      size_t top;
      };

      // 类外定义成员函数,需指定类域Stack::
      void Stack::Init(int n)
      {
      array = (int*)malloc(sizeof(int) * n);
      if (nullptr == array)
      {
      perror("malloc申请空间失败");
      return;
      }
      capacity = n;
      top = 0;
      }

      int main()
      {
      Stack st;
      st.Init(); // 调用Stack类的Init函数
      return 0;
      }


    2. 类的实例化

      2.1 实例化概念

    • 定义:用类类型在物理内存中创建对象的过程,称为类实例化出对象。
    • 核心区别:
      • 类:抽象描述(如建筑设计图),仅声明成员变量和成员函数,不分配物理空间,不能存储数据。
      • 对象:类的具体实例(如按设计图建造的房子),分配物理空间,存储成员变量,可调用成员函数。

    • 特性:一个类可实例化多个对象,每个对象有独立的成员变量存储空间,成员函数共享(存储在代码段)。
    • 示例代码: #include<iostream>
      using namespace std;

      class Date
      {
      public:
      void Init(int year, int month, int day)
      {
      _year = year;
      _month = month;
      _day = day;
      }

      void Print()
      {
      cout << _year << "/" << _month << "/" << _day << endl;
      }
      private:
      // 成员变量仅声明,实例化对象时才分配空间
      int _year;
      int _month;
      int _day;
      };

      int main()
      {
      Date d1; // 实例化对象d1
      Date d2; // 实例化对象d2
      d1.Init(2024, 3, 31);
      d1.Print(); // 输出2024/3/31
      d2.Init(2024, 7, 5);
      d2.Print(); // 输出2024/7/5
      return 0;
      }

      2.2 对象大小计算

      2.2.1 对象存储规则
    • 对象仅存储成员变量,成员函数存储在代码段(共享),无需每个对象单独存储(避免空间浪费)。
    • 空类(无成员变量)的对象大小为 1 字节,仅用于占位标识对象存在,不存储实际数据。
      2.2.2 内存对齐规则

         对象的成员变量存储需遵循内存对齐,规则如下:

  • 第一个成员变量存储在偏移量为 0 的地址处。
  • 其他成员变量需对齐到 “对齐数” 的整数倍地址处;对齐数 = 编译器默认对齐数与成员大小的较小值(VS 默认对齐数为 8)。
  • 对象总大小为 “最大对齐数”(所有成员对齐数的最大值)的整数倍。
  • 嵌套结构体:嵌套的结构体对齐到自身最大对齐数的整数倍,对象总大小为包含嵌套结构体在内的所有最大对齐数的整数倍。
  •   2.2.3 示例代码与计算

    #include<iostream>
    using namespace std;

    // 计算对象大小
    class A
    {
    public:
    void Print()
    {
    cout << _ch << endl;
    }
    private:
    char _ch; // 大小1字节,对齐数1
    int _i; // 大小4字节,对齐数4(VS默认8,取较小值4)
    };

    class B
    {
    public:
    void Print()
    {
    // 无成员变量
    }
    };

    class C
    {}; // 空类

    int main()
    {
    A a;
    B b;
    C c;
    cout << sizeof(a) << endl; // 结果8(1+3(填充)+4=8,最大对齐数4,8是4的整数倍)
    cout << sizeof(b) << endl; // 结果1(无成员变量,占位字节)
    cout << sizeof(c) << endl; // 结果1(空类,占位字节)
    return 0;
    }


    3. this 指针

      3.1 核心作用

    • 解决成员函数区分不同对象的问题:多个对象共享成员函数,this指针指向当前调用成员函数的对象,成员函数通过this指针访问当前对象的成员变量。

      3.2 特性

    • 隐含参数:编译器会在成员函数的形参第一个位置隐含添加当前类类型* const this(如Date类的Init函数真实原型为void Init(Date* const this, int year, int month, int day))
    • 访问方式:
      • 函数体内可显式使用this指针访问成员变量(如this->_month = month),也可省略(如_year = year,编译器自动补全this->)。
      • 不可在实参或形参位置显式声明this指针(编译器自动处理)。
    • this指针的const属性:this指针本身不可修改(Date* const this),不能给this指针赋值(如this = nullptr编译报错)。

      3.3 示例代码

    #include<iostream>
    using namespace std;

    class Date
    {
    public:
    void Init(int year, int month, int day)
    {
    // this = nullptr; // 编译报错:this指针不可修改
    _year = year; // 等价于this->_year = year
    this->_month = month;
    this->_day = day;
    }

    void Print()
    {
    cout << _year << "/" << _month << "/" << _day << endl;
    }
    private:
    int _year;
    int _month;
    int _day;
    };

    int main()
    {
    Date d1;
    // 实际调用:d1.Init(&d1, 2024, 3, 31)
    d1.Init(2024, 3, 31);
    d1.Print(); // 输出2024/3/31
    return 0;
    }

    3.4 经典例题(this 指针相关)

    例题 1

    #include<iostream>
    using namespace std;

    class A
    {
    public:
    void Print()
    {
    cout << "A::Print()" << endl;
    }
    private:
    int _a;
    };

    int main()
    {
    A* p = nullptr;
    p->Print(); // 运行结果:正常运行(输出A::Print())
    return 0;
    }

    • 解析:Print函数未访问成员变量,无需通过this指针解引用,仅需找到函数地址(存储在代码段),因此空指针调用不崩溃。
    例题 2

    #include<iostream>
    using namespace std;

    class A
    {
    public:
    void Print()
    {
    cout << "A::Print()" << endl;
    cout << _a << endl; // 访问成员变量,需this->_a
    }
    private:
    int _a;
    };

    int main()
    {
    A* p = nullptr;
    p->Print(); // 运行结果:运行崩溃(空指针解引用)
    return 0;
    }

    • 解析:Print函数访问_a,等价于this->_a,而p为nullptr,this指针为nullptr,解引用空指针导致崩溃。
    例题 3
    • 问题:this 指针存储在内存哪个区域?(答案:A. 栈)
    • 解析:this指针是成员函数的隐含形参,形参存储在栈区,因此this指针位于栈区。(有时候也会说this在寄存器中)

    4. C++ 与 C 语言实现 Stack 对比(封装特性体现)

      4.1 核心差异(面向对象封装特性)

    对比维度C 语言实现C++ 实现
    数据与函数组织 数据(结构体)与函数分离,函数需显式传结构体指针 数据与函数封装在类内,通过访问限定符控制访问
    访问控制 可直接修改结构体成员(如ps->top = -1),无权限限制 成员变量设为private,不可直接修改,需通过成员函数操作
    语法便捷性 需typedef定义结构体类型,函数需传指针 类名直接作为类型,成员函数隐含this指针,无需传对象地址
    其他特性 无缺省参数等便捷语法 支持缺省参数(如Init默认容量 4)等语法

      4.2 C 语言实现 Stack 代码

    #include<stdio.h>
    #include<stdlib.h>
    #include<stdbool.h>
    #include<assert.h>

    typedef int STDataType;
    typedef struct Stack
    {
    STDataType* a;
    int top;
    int capacity;
    }ST;

    void STInit(ST* ps)
    {
    assert(ps);
    ps->a = NULL;
    ps->top = 0;
    ps->capacity = 0;
    }

    void STDestroy(ST* ps)
    {
    assert(ps);
    free(ps->a);
    ps->a = NULL;
    ps->top = ps->capacity = 0;
    }

    void STPush(ST* ps, STDataType x)
    {
    assert(ps);
    if (ps->top == ps->capacity)
    {
    int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
    STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
    if (tmp == NULL)
    {
    perror("realloc fail");
    return;
    }
    ps->a = tmp;
    ps->capacity = newcapacity;
    }
    ps->a[ps->top] = x;
    ps->top++;
    }

    bool STEmpty(ST* ps)
    {
    assert(ps);
    return ps->top == 0;
    }

    void STPop(ST* ps)
    {
    assert(ps);
    assert(!STEmpty(ps));
    ps->top–;
    }

    STDataType STTop(ST* ps)
    {
    assert(ps);
    assert(!STEmpty(ps));
    return ps->a[ps->top – 1];
    }

    int STSize(ST* ps)
    {
    assert(ps);
    return ps->top;
    }

    int main()
    {
    ST s;
    STInit(&s);
    STPush(&s, 1);
    STPush(&s, 2);
    STPush(&s, 3);
    STPush(&s, 4);
    while (!STEmpty(&s))
    {
    printf("%d\\n", STTop(&s));
    STPop(&s);
    }
    STDestroy(&s);
    return 0;
    }

      4.3 C++ 实现 Stack 代码

    #include<iostream>
    using namespace std;
    #include<assert.h>

    typedef int STDataType;
    class Stack
    {
    public:
    void Init(int n = 4)
    {
    _a = (STDataType*)malloc(sizeof(STDataType) * n);
    if (nullptr == _a)
    {
    perror("malloc申请空间失败");
    return;
    }
    _capacity = n;
    _top = 0;
    }

    void Push(STDataType x)
    {
    if (_top == _capacity)
    {
    int newcapacity = _capacity * 2;
    STDataType* tmp = (STDataType*)realloc(_a, newcapacity * sizeof(STDataType));
    if (tmp == NULL)
    {
    perror("realloc fail");
    return;
    }
    _a = tmp;
    _capacity = newcapacity;
    }
    _a[_top++] = x;
    }

    void Pop()
    {
    assert(_top > 0);
    –_top;
    }

    bool Empty()
    {
    return _top == 0;
    }

    int Top()
    {
    assert(_top > 0);
    return _a[_top – 1];
    }

    void Destroy()
    {
    free(_a);
    _a = nullptr;
    _top = _capacity = 0;
    }
    private:
    STDataType* _a;
    size_t _capacity;
    size_t _top;
    };

    int main()
    {
    Stack s;
    s.Init();
    s.Push(1);
    s.Push(2);
    s.Push(3);
    s.Push(4);
    while (!s.Empty())
    {
    printf("%d\\n", s.Top());
    s.Pop();
    }
    s.Destroy();
    return 0;
    }


    5. 总结(类和对象・上核心)

  • 核心是 C++ 面向对象的封装特性:用class/struct定义类,通过public/private/protected访问限定符控制成员访问(成员变量私有、成员函数公有),实现数据和方法的封装;
  • 类是抽象模板(不占内存),实例化出的对象才分配空间,对象大小仅算成员变量(遵循内存对齐,空类占 1 字节占位);
  • this指针是成员函数的隐含参数,指向当前调用对象,用于区分不同对象的成员,是类成员函数工作的核心机制。
  • 赞(0)
    未经允许不得转载:171主机测评 » C++类和对象(上)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址