C++:类和对象(上)---初步认识类和this指针

简介: C++:类和对象(上)---初步认识类和this指针

面向过程和面向对象

C语言是一门面向过程的语言关注的是过程,确认一个问题求解的步骤,再一步一步对它进行解决

C++是一门基于面向对象的语言,它更关注的是对象,将一个事情分成不同的对象,再用对象完成问题的解决

类的引入

在C语言中有结构体,但结构体中只能定义变量,而在C++中还可以定义函数,例如,数据结构中的链表栈队列等函数都需要定义成员,在外部定义函数,通过函数改变具体数据结构的内容,而在C++中,结构体中也是可以定义函数的,这是C++对C的提升

类的定义

class className
{
// 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号
  1. class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
  2. 类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。

类定义的方式

  1. 声明和定义都放在类体中,其中需要注意的是,如果成员函数在类内定义,那么编译器可能会把成员函数当作内联函数来处理

具体定义形式如下:

#include <iostream>
using namespace std;
class Time
{
public:
  void settime()
  {
    cin >> hour >> minute >> second;
  }
  void show()
  {
    cout << hour << endl;
    cout << minute << endl;
    cout << second << endl;
  }
private:
  int hour;
  int minute;
  int second;
};
int main()
{
  Time t1;
  t1.settime();
  t1.show();
  return 0;
}
  1. 类声明放在.h文件中,定义放在.cpp文件中,一般来说更希望使用这种写法

类的访问限定符

关于访问限定符:

  1. public修饰的成员在类外可以直接被访问
  2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. 如果后面没有访问限定符,作用域就到 } 即类结束。
  5. class的默认访问权限为private,struct为public(因为struct要兼容C)

类的实例化

通常来说,用类的类型创建出对象的过程就是类的实例化

我们可以这样理解,类就是一张设计图纸,而把设计图纸变现才能有实际空间,占用内存空间,因此,类本身是没有空间的,只有把类实例化出一个对象才有实际的空间

类对象模型

类对象的大小

既然创建出了一个类,那么这个类的对象中包含了什么,一个类的大小又如何计算

假设我们有下面的代码

#include <iostream>
using namespace std;
class date
{
public:
  void print()
  {
    cout << "void print()" << a << endl;
  }
private:
  int a;
};
int main()
{
  date d1;
  cout << sizeof(date) << endl;
  cout << sizeof(d1) << endl;
  cout << &d1 << endl;
}

那么类的大小究竟是如何计算的?下面就进行类的存储方式的介绍

在C++中,对象的存储方式是这样的:

因此,类中的成员是独立存在的,而类的成员函数是放在公共代码区的

类的成员是可以独立相加的,它的规则其实和结构体的计算相同,都是遵循内存对齐规则进行相加

而公共代码区的证明也可以通过汇编来看

从中可以看出,类的成员函数在调用的时候确实是在一块固定的公共代码区进行的调用,因此就理解了类在计算机内部是如何存储的

但依旧有两个特例:

// 类中仅有成员函数
class A 
{
public:
  void f2() {}
};
// 类中什么都没有---空类
class B
{};
int main()
{
  cout << sizeof(A) << endl;
  cout << sizeof(B) << endl;
}

当类中没有成员或直接就是空类的时候,它们的大小均为1,这个1没有实际意义,只是用来占位,证明这里曾经存在过A和B

this指针

C++中还引入了this指针的概念,这是一个较为隐秘的内容

this指针引入的原因

当我们创建好类,要用类定义变量时,再利用变量调用公共代码区的成员函数时,你是否有这样的问题:函数体中并没有写到不同对象的区分,那么不同的变量调用函数时,函数是如何确定应该作用于哪个变量中?

这就是this指针的功劳

C++中引入了this指针,这个this指针是每一个非静态的成员函数的指针参数,也就是说,每一个成员函数的实际参数要比它表面的参数多一个指针参数,而这个指针存在的意义就是在调用时指向当前对象,这个操作是编译器自动完成,整个过程是透明的,用户不需要完成这些操作

下面代码和图解来解释一下:

现在有这样的代码:

#include <iostream>
using namespace std;
class date
{
public:
  void print(int year)
  {
    a = year;
  }
private:
  int a;
};
int main()
{
  date d1;
  date d2;
  d1.print(2004);
  d2.print(2003);
}

表面上看,print函数的参数只有一个year,d1和d2调用print函数的时候传递的也只有一个参数,但实际上在编译器内部是这样的

this指针的特性

  1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
  2. 只能在“成员函数”的内部使用
  3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
  4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递

关于理解this指针

// 下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
  void Print()
  {
    cout << "Print()" << endl;
  }
private:
  int _a;
};
int main()
{
  A* p = nullptr;
  p->Print();
  return 0;
}

编译运行可以正常运行,原因就在于上面讲的成员函数存储位置的问题

本题的问题点在于,这里p是空指针,我直接解引用空指针难道不是错误的吗?

其实不然,这里根本没有解引用,首先要清楚,解引用的意思是我要用一个指针去访问这块空间区域内的某个内容,这个过程是解引用,而Print函数是存在对象里面的吗?很明显并不是,成员函数是存储在公共代码区的,这里看似解引用实际上只是普通的函数调用,通过汇编也可以很好的解释这个现象

这里我们把private注释掉,使得指针可以访问对象内部的成员_a

最下方的汇编才是解引用,我们要访问对象内部的数据_a,当然空指针是不可以解引用的…

但重点更好的说明了,上面的语句只是单纯调用了Print函数而已,第一句是把this指针传递给了函数,也就是所谓的传参,第二句call则是所谓的调用,执行函数的本质就是传参和调用,从汇编上可以更好的理解这个问题

如果你对这里还是有所疑问,再来看下面的例子对比

class Date
{
public:
  void Print()
  {
    cout << this << endl;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date d1;
  d1.Print();
  Date* p2 = nullptr;
  p2->Print();
  return 0;
}

上面的代码意思是把调用Print函数打印this指针的值,这个this指针其实就是对象的地址,这个代码可以更好的解释我们前面所说明的,成员函数的调用就是传递了this指针参数后进行的函数调用

如果理解了上面的题目,下面还有一道类似的题:

class A
{
public:
  void PrintA()
  {
    cout << _a << endl;
  }
private:
  int _a;
};
int main()
{
  A* p = nullptr;
  p->PrintA();
  return 0;
}

如果你理解了上面的题目,那么下面的题就很简单了,这里唯一的区别是Print函数中要打印成员的值,而实际上打印的是this->_a,因此,传递参数的this是nullptr,所以程序会崩溃

相关文章
|
2天前
|
存储 C++ 索引
C++函数指针详解
【10月更文挑战第3天】本文介绍了C++中的函数指针概念、定义与应用。函数指针是一种指向函数的特殊指针,其类型取决于函数的返回值与参数类型。定义函数指针需指定返回类型和参数列表,如 `int (*funcPtr)(int, int);`。通过赋值函数名给指针,即可调用该函数,支持两种调用格式:`(*funcPtr)(参数)` 和 `funcPtr(参数)`。函数指针还可作为参数传递给其他函数,增强程序灵活性。此外,也可创建函数指针数组,存储多个函数指针。
|
10天前
|
编译器 C++
【C++核心】指针和引用案例详解
这篇文章详细讲解了C++中指针和引用的概念、使用场景和操作技巧,包括指针的定义、指针与数组、指针与函数的关系,以及引用的基本使用、注意事项和作为函数参数和返回值的用法。
14 3
|
1月前
|
C++
C++(十一)对象数组
本文介绍了C++中对象数组的使用方法及其注意事项。通过示例展示了如何定义和初始化对象数组,并解释了栈对象数组与堆对象数组在初始化时的区别。重点强调了构造器设计时应考虑无参构造器的重要性,以及在需要进一步初始化的情况下采用二段式初始化策略的应用场景。
|
1月前
|
存储 编译器 C++
C ++初阶:类和对象(中)
C ++初阶:类和对象(中)
|
1月前
|
C++
C++(十八)Smart Pointer 智能指针简介
智能指针是C++中用于管理动态分配内存的一种机制,通过自动释放不再使用的内存来防止内存泄漏。`auto_ptr`是早期的一种实现,但已被`shared_ptr`和`weak_ptr`取代。这些智能指针基于RAII(Resource Acquisition Is Initialization)原则,即资源获取即初始化。RAII确保对象在其生命周期结束时自动释放资源。通过重载`*`和`-&gt;`运算符,可以方便地访问和操作智能指针所指向的对象。
|
1月前
|
C++
C++(九)this指针
`this`指针是系统在创建对象时默认生成的,用于指向当前对象,便于使用。其特性包括:指向当前对象,适用于所有成员函数但不适用于初始化列表;作为隐含参数传递,不影响对象大小;类型为`ClassName* const`,指向不可变。`this`的作用在于避免参数与成员变量重名,并支持多重串联调用。例如,在`Stu`类中,通过`this-&gt;name`和`this-&gt;age`明确区分局部变量与成员变量,同时支持链式调用如`s.growUp().growUp()`。
|
2月前
|
存储 安全 C++
C++:指针引用普通变量适用场景
指针和引用都是C++提供的强大工具,它们在不同的场景下发挥着不可或缺的作用。了解两者的特点及适用场景,可以帮助开发者编写出更加高效、可读性更强的代码。在实际开发中,合理选择使用指针或引用是提高编程技巧的关键。
24 1
|
2月前
|
安全 NoSQL Redis
C++新特性-智能指针
C++新特性-智能指针
|
2月前
|
编译器 C++
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
|
2月前
|
存储 C++
c++学习笔记06 指针
C++指针的详细学习笔记06,涵盖了指针的定义、使用、内存占用、空指针和野指针的概念,以及指针与数组、函数的关系和使用技巧。
30 0
下一篇
无影云桌面