C++类基础概念:定义、实例化和this指针

目录

[1. 类的定义](#1. 类的定义)

[1.1 定义格式](#1.1 定义格式)

[1.2 访问限定符](#1.2 访问限定符)

[1.3 类域](#1.3 类域)

[2. 类的实例化](#2. 类的实例化)

[2.1 实例化对象大小](#2.1 实例化对象大小)

[3. 隐含this指针](#3. 隐含this指针)


1. 类的定义

C++的结构体在C语言结构体的基础上增加了函数和访问控制等操作,而C++的类和C++的结构体除了默认访问权限完全相同。

(另外在struct方面,C++还实现了声明时可以省略写struct,直接写结构体名的步骤:

cpp 复制代码
struct Student {
    char name[20];
    int age;
};

// 定义变量时不需要struct
Student s1;
Student* p;   

说回正题:

  • 类是一个自定义的数据类型,通常用于将功能相近的数据和操作绑定在一起,本质是结构体和函数指针的语法糖。
  • 对象是类的具体实现,也称作类的实例化,是在内存中真实开辟出空间来存储的。

举个例子,类是建筑的蓝图,而对象则是根据这个蓝图创建出的一个个房屋,是具体存在的。

1.1 定义格式

类由以下部分组成:

  • 关键字class
  • 类名Stack(自定义名称,类名即类型)
  • 类的主体(存放在{}中,包括成员变量和成员函数)
  • 分号 (易漏)

C++中struct 也可以定义类,但struct的默认访问权限是public,class是private ,为了保护数据安全,大部分情况下,类都使用class实现。

cpp 复制代码
class Stack // 关键字和类名
{
private:

    // 成员变量
    int* arr;
    int _size;
    int _capacity;

public:

    // 成员函数
    void Print();
    void Destroy();
    void Push(int x);
};
// 分号

补充知识:

  1. C++中类的成员变量名通常会加上特殊标识 方便区分,例如下划线_或者m(表示member)
  2. 声明和定义都在类内的函数默认为inline函数
  3. 类内部成员的声明顺序没有强制要求

1.2 访问限定符

访问限定符包括:public,private,protected

访问级别 自己(本类) 子类(派生类) 外部
public 能访问 能访问 能访问
protected 能访问 能访问 不能访问
private 能访问 不能访问 不能访

protected和private的区别会在继承显著体现。

用法和注意事项:

  • 访问限定符的作用域:始于该限定符 ,止于下一个访问限定符

  • class默认权限为private ,struct默认权限为public

  • 一般来说,我们将成员变量设为private成员函数设为public,这样既能保护变量安全,又能通过函数接口来操作对象。

1.3 类域

类构成了一个独立 的作用域,必须通过**作用域解析运算符 ::**才能访问其成员。

设置类域是为了方便编译器查找 ,指定了类域后编译器在编译时就会自动去对应的类中查找 ;如果未指定又试图访问类内部成员,那么编译器只会在全局查找 对应成员的声明和定义,最终导致无法找到而报错

另外,一般来说类中成员函数的声明和定义是分离的 (除了有意设置为inline函数),而在类外定义就需要使用作用域操作符::

**Q:**声明和定义有什么区别?

A: 声明无需分配内存空间,而定义则需分配内存空间。

举个例子:

cpp 复制代码
#include<iostream>
using namespace std;

class Stack
{
public:
    void Init(int n = 4);
private:
    // 只是声明,未开空间
    int* array;
    size_t capacity;
    size_t top;
};

// 声明和定义分离,需要指定类域
void Stack ::Init(int n)
{
    array = (int*)malloc(sizeof(int) * n);
    if (nullptr == array)
    {
        perror("malloc申请空间失败");
        return;
    }

    capacity = n;
    top = 0;
}

C++类成员的三种访问方式:

cpp 复制代码
class Student {
public:
    static string school;
    string name;
    void print();
};

// 静态成员变量类外初始化
string Student::school = "默认学校";

// 类外定义成员函数
void Student::print() {
    cout << name << endl;
}

int main() {
    // 1. 通过对象
    Student s;
    s.name = "Tom";
    s.print();
    
    // 2. 通过指针
    Student* p = &s;
    p->name = "Jerry";
    p->print(); // 编译器会转化为print(p)
    
    // 3. 通过类名(静态成员)
    Student::school = "xx学校";
    
    return 0;
}

2. 类的实例化

一个类可以实例化出多个对象,这些对象开辟了空间,是可以直接在内存中找到的。

2.1 实例化对象大小

对象在内存中的存储方式和struct类似,都是通过内存对齐的方式存储的。

**Q:**为什么存在内存对齐规则?

A: CPU读取数据时并非从任意位置开始读取任意大小,而是按照固定对齐方式从整数倍地址处读取固定长度的数据块。若数据未按内存对齐规则存储,可能导致CPU需要分多次读取并拼接数据,从而降低访问效率。内存对齐机制的核心作用就是优化CPU的数据读取性能。

但是对象仅仅会为成员变量多次开辟空间,对于成员函数而言,每个对象的成员函数功能都是一样的,没必要单独开辟多个空间存储函数指针,因此计算对象大小时仅需关注成员变量即可。

内存对齐规则和C的结构体规则类似,详细介绍可以看博主的这篇博客:

C语言:结构体(自定义类型)-CSDN博客

嵌套类和嵌套结构体的计算方式相同。

Q: 如果一个对象中什么成员变量也没有存放,那么大小为多少呢?

cpp 复制代码
class A
{
public:
    void Print();
};

int main()
{
    A a;
    cout << sizeof(a) << endl;
    return 0;
}

A: 实例化对象就需要分配内存空间,因此设置为1个字节,仅仅作为占位符使用,不存储实际数据。运行后如下图:

3. 隐含this指针

Q:类内声明 的成员函数,放在类外定义 时并没有通过限定符::访问,那么成员函数如何区分不同对象呢?

A: 因为在编译器编译后,类的成员函数会默认在第一个形参的位置增加一个指向当前对象地址的this指针,类中的成员函数,本质都是通过this指针访问成员变量的。

如下:

cpp 复制代码
// 代码显式写的
void A::Print()
{
    cout << _size << end;
    cout << _capacity << endl;
}

// 编译器底层转换
void A::Print(A* const this)
{
    cout << this->_size << endl;
    cout << this->_capacity << endl;
}

注意事项:

C++规定不能实参和形参 位置显式写this指针(编译器在编译时会自动处理),也不能传入对象的地址;但是可以在函数内部显式使用this指针。

如下:

cpp 复制代码
// 形参部分不能写
void A::Print()
{
    // 可以在内部显式使用
    cout << this->_size << endl;
    cout << this->_capacity << endl;
}

this指针本质上是函数的形参,因此this指针是存储在栈中的。

// 感谢阅读,欢迎讨论交流= ̄ω ̄= 封面是soyo酱🤗

相关推荐
.柒宇.2 小时前
Java八股之反射
java·开发语言
环流_2 小时前
多线程1(面试题--常见的线程创建方式)
java·开发语言·面试
不想写代码的星星2 小时前
C++17 string_view 观察报告:好用,但有点费命
c++
努力努力再努力wz3 小时前
【Linux网络系列】深入理解 I/O 多路复用:从 select 痛点到 poll 高并发服务器落地,基于 Poll、智能指针与非阻塞 I/O与线程池手写一个高性能 HTTP 服务器!(附源码)
java·linux·运维·服务器·c语言·c++·python
努力努力再努力wz3 小时前
【Linux网络系列】万字硬核解析网络层核心:IP协议到IP 分片重组、NAT技术及 RIP/OSPF 动态路由全景
java·linux·运维·服务器·数据结构·c++·python
Han_han9193 小时前
常用API:
java·开发语言
minji...3 小时前
Linux 线程同步与互斥(四) POSIX信号量,基于环形队列的生产者消费者模型
linux·运维·服务器·c语言·开发语言·c++
Highcharts.js3 小时前
在 React 中使用 useState 和 @highcharts/react 构建动态图表
开发语言·前端·javascript·react.js·信息可视化·前端框架·highcharts
likerhood3 小时前
java中的return this、链式编程和Builder模式
java·开发语言