【C++】类和对象(上)

简介: 【C++】类和对象(上)

一、面向过程和面向对象初步认识


C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。


C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。


C语言和C++ 写一个外卖系统的差别:


C语言只能利用关键字struct 去写(面向一系列过程),而C++可以针对外卖系统写出商家、骑手等几个类。

1669443734152.jpg


二、类的引入


C语言中,结构体中只能定义变量,在C++中,结构体做了以下改动


1、C++兼容C语言中 struct的用法


2、C++同时对struct进行了升级, 把struct 升级成了类


3、结构体名称可以做类型


4、结构体里面可以定义函数


struct 不加访问限定符,默认是public


class 不加访问限定符,默认是private


class Student
{
public://如果没加public,class默认是私有的,私有的在类外一般不能访问
  //类体:由成员函数和成员变量组成
  void Init(const char* name, const char* gender, int age)
  {
  strcpy(_name, name);
  strcpy(_gender, gender);
  _age = age;
  }
  void Print()
  {
  cout << _name << " " << _gender << " " << _age << endl;
  }
  //并不是必须加
  //习惯加_,用来标识成员变量
private:
  char _name[20];
  char _gender[3];
  int _age;
protected:
};
struct ListNode
{
  int val;
  //C语言要这样写struct ListNode* next;
  ListNode* next;
};
int main()
{
  Student s1;
  Student s2;
  s1.Init("张三", "男",18);
  s1.Print();
  return 0;
}


三、类的定义


3.1、最简单的类示例:


class className
{
 // 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号

解读:

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号。 类中的元素称为类的成员:类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数。


3.2、类的两种定义方式:


1. 声明和定义全部放在类体中,需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。


2. 声明放在.h文件中,类的定义放在.cpp文件中


一般情况下,更期望采用第二种方式。


以下第二种方式比较推荐:


1669443784080.jpg


四、类的访问限定符及封装


4.1、访问限定符


和开头所介绍的一样C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其 接口提供给外部的用户使用。


1669443798083.jpg


【访问限定符说明】


1. public修饰的成员在类外可以直接被访问


2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)


3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止


4. class的默认访问权限为private,struct为public(因为struct要兼容C


注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别


4.2、封装


面向对象的三大特性:封装、继承、多态。


什么是封装呢?


封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。


封装本质上是一种管理,以兵马俑为例子:我们如何管理兵马俑呢?比如如果什么都不管,兵马俑就被随意破坏了。那么我们 首先建了一座房子把兵马俑给封装起来。但是我们目的全封装起来,不让别人看。所以我们开放了售票通道,可以买票突破封装在合理的监管机制下进去参观。类也是一样,我们使用类数据和方法都封装到一下。 不想给别人看到的,我们使用protected/private把成员封装起来。开放一些共有的成员函数对成员合理的访问。所以封装本质是一种管理。

class Stack
{
public:
  void Init()
  {
  }
  void Push(int x)
  {
  }
  int StackTop()
  {
  }
private:
  int* _a;
  int _top;
  int _capacity;
};
int main()
{
  Stack st;
  st.Init();
  st.Push(1);
  return 0;
}


4.3类的作用域


类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域。而且还得是公有(public)的才能这样访问。和之前的命名空间访问有些像。


五、类的实例化


5.1、含义


用类类型创建对象的过程,称为类的实例化


1. 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它


2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量


3. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什 么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间


1669443853022.jpg


六.类对象模型


6.1、类对象的存储方式猜测


(1)对象中包含类的各个成员

1669443869011.jpg

缺陷:每个对象中成员变量是不同的,但是调用同一份函数,如果按照此种方式存储,当一个类创建多 个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。


(2)只保存成员变量,成员函数存放在公共的代码段


1669443882791.jpg

1669443889963.jpg


利用上面的栈,在public区域写一个init初始化函数,然后在mian函数里建立两个对象,通过调试查看


1669443899020.jpg


检查汇编,发现st1和st2调用的是同一个函数


1669443906925.jpg


所以证明了类对象的储存规则大概就是:只保存成员变量,成员函数存放在公共的代码段这种。


6.2、计算类的大小


示例1:类中既有成员变量,又有成员函数

#include"Stack.h"
//类大小的计算不考虑成员函数
class A1 {
public:
       void f1() {}
private:
       int _a;
       char ch;
};
int main()
{
       cout << sizeof(A1) << endl;
       return 0;
}

类中有int 类型的 _a,和char类型的 ch;遵循内存对齐的规则,而且不计算成员函数大小,最终计算结果就是

1669443935587.jpg


示例2:类中仅有成员函数

#include"Stack.h"
//类大小的计算不考虑成员函数
class A1 {
public:
       void f1() {}
};
int main()
{
       cout << sizeof(A1) << endl;
       return 0;
}

1669443951831.jpg


示例3:类中什么都没有--空类

#include"Stack.h"
//类大小的计算不考虑成员函数
class A1 {
public:
};
int main()
{
       cout << sizeof(A1) << endl;
       return 0;
}

1669443968251.jpg


总结:一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。


七、this指针


7.1、我们先来定义一个日期类Date


日期类的代码如下:

class Date
{
public:
  void Print()//this是一个新增的关键字,是一个形参
  {
  cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
  }
  void Init(int year, int month, int day)
  {
  _year = year;
  _month = month;
  _day = day;
  }
private:
  int _year; // 年
  int _month; // 月
  int _day; // 日
};
int main()
{
  Date d1;
  d1.Init(2022, 5, 11);
  Date d2;
  d2.Init(2022, 5, 12);
  d1.Print();
  d2.Print();
  return 0;
}


首先我们需要知道;一个类可以创建多个对象,未初始化时是随机值


1669443989895.jpg


我们定义了d1和d2对象,d1和d2都调用了Print函数,那么编译器怎么区别d1和d2应该调用哪个Print函数呢?


解答:C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参 数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该 指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。


1669444000862.jpg

1669444007784.jpg

其中:


this指针是隐藏的形参


显示调用编译器会报错,因为这是编译器的工作,我们不能把编译器的工作抢了。


7.2、this指针的特性


1. this指针的类型:类的类型* const


2. 只能在“成员函数”的内部使用


3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this 形参。所以对象中不存储this指针。


4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递


对于第一点:


1669444024500.jpg


void Print() 等价于上面的这种写法


注意:


const在这是修饰this指针本身,也就是说指针本身不能被修改,但指针指向的内容可以被修改


const在*之前--修饰的是指针指向的内容


const在*之后--修饰的是指针本身


对于第三点:我们打印一下d1和d2的地址

void Print()//this是一个新增的关键字,是一个形参
       {
              cout << this << endl;
              cout << _year << "-" << _month << "-" << _day << endl;
       }

1669444037580.jpg


可以看出this的地址和d1,d2的地址是对应一致的。即this指针的地址和d的地址是一样的。


7.3、关于this指针的两个问题探讨


1、this指针可以为空吗?


来一起看看这段代码


1669444059786.jpg


分析:


1、p是一个空指针,其解引用编译阶段看不出错误,运行时才会报错


2、对象里面只有成员变量,p->show()没有解引用,相当于是空指针去访问show()。


答案选C--正常运行


所以:this指针可以为空指针,因为虽然this是不可以被修改的但是可以被初始化。


2、this指针存在哪里?


this是形参,形参是栈帧,所以严格来说this是在存在栈上的,this指针也可能在寄存器,优化会更快。

相关文章
|
25天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
38 2
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
83 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
80 4
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
88 4
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
31 4
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
32 4
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
26 1
|
2月前
|
存储 编译器 C语言
【C++打怪之路Lv3】-- 类和对象(上)
【C++打怪之路Lv3】-- 类和对象(上)
18 0
|
2月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
2月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)