【C++打怪之路Lv3】-- 类和对象(上)

简介: 【C++打怪之路Lv3】-- 类和对象(上)

面向对象和面向过程的认识


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

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

那什么是过程?什么是对象呢?

我们在生活中举例子:比如我要去烧一壶开水

对象:我,热水壶,水,排插/插座,热水壶托盘

过程:把水接到水壶里 --> 关闭水壶盖 --> 把热水壶放到托盘中 --> 连接好插排/插座 --> 按下热水壶开关 --> 静等几分钟 -->一壶水烧开了



类的引入(以下以Stack为例)

在C语言中,我们学过结构体,但在C语言的结构体当中,只能由成员变量组成,回顾一下怎么用C语言实现(Stack)栈的呢?

然而在C++当中,结构体里不仅可以定义成员变量,还可以定义成员函数,那就是“类”

类的介绍

在C++当中,喜欢把struct写成class(类),什么是类?

class ClassName
{
    //由 成员变量 和 成员函数 来组成
};

注:1、class和struct相似,   2、ClassName是类的名称,

      3、名称后需跟中括号{},4、右中括号后还有分号 ( ; )

那么在C++当中关于栈的类是怎么定义的呢?

类的第一种定义方法(用这种方便)

在class(类)中声明和定义成员变量和成员函数(可以在class中具体实现成员函数)

注:(成员函数如果放在类体中,编译器可能会当成内联函数处理)

(关于访问权限我们在后续中会讲到,这里先做铺垫)

类的第二种定义方法:类的作用域(实际期望用这种)

类定义了一个新的作用域,类的所有成员都在类的作用域中。

在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。

在class(类)中定义成员变量和定义成员函数,

在.cpp文件中声明成员函数  (也就是通过类和作用域限定符指定实现Stack类中的某个函数)

成员变量命名规则

建议①

成员变量用 _year 表示

建议②

成员变量用 前缀(m_year) 或者 后缀(例如:year_m)来表示


类的访问限定符


访问限定符的介绍

C++实现封装(文章下面会讲到)的方式:

用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

访问限定符包括:public(公有),protected(保护),private(私有)

注: 1、public修饰的成员在类外是直接可以访问的

       2、private修饰的成员在类外不能直接被访问(protect和private是类似的)

       3、访问权限作用域从访问权限开始到下一个访问权限出现结束

       4、 如访问没有限定符,那就到 } 结束

       5、class默认访问权限是private,struct默认访问权限是public(struct兼容C语言)


封装


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

在类和对象阶段,主要是研究类的封装特性,那什么是封装呢?

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

封装本质上是一种管理,让用户更方便使用类

就像上面说的 面向对象和面向过程的认识 一样

烧一壶开水,我们只需关心把水装进水壶,再按下按钮就可以了

并不关心开水是怎么烧开的,它的内部用什么装置和线路组成的(封装)

用户只需要与厂家提供的按钮,热水壶等显而易见直接实现的外部装置之间进行交互

在C++语言中实现封装,

可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。


类的实例化


概念

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

类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,

定义出一个类并没有分配实际的内存空间来存储它

打个比方:

类实例化出对象就像现实中使用建筑设计图建造出房子,

类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在

同样类也只是一个设计实例化出的对象才能实际存储数据,占用物理空间

类  和  对象 是 一对多的关系

一个类可以有多个对象,比方类是设计图,对象是通过设计建出来的房子,

一个设计图可以设计建造出多个类似的房子

理解类的实例化(含图片和代码)

举例:Data日期类

class Data
{
public:
  void Init(int yaer, int month, int day)
  {
    m_year = yaer;
    m_month = month;
    m_day = day;
  }
 
private:
  int m_year; //年
  int m_month;//月
  int m_day;  //日
};
 
int main()
{
  Data s1;    //定义(实例化)对象
  Data s2;    //定义(实例化)对象
  s1.Init(2024, 4, 25);
  s1.Init(2024, 4, 25);
 
  Data s3;    //定义(实例化)对象
  Data s4;    //定义(实例化)对象
 
  s1._yaer++;   //成员变量的权限为private
  s2._yaer++;
  return 0;
}


对象模型


计算类对象大小

(看下面Data类,类A、B、C)

class Data
{
public:
  void Init(int yaer, int month, int day)
  {
    _year = yaer;
    _month = month;
    _day = day;
  }
 
private:
  int _year;
  int _month;
  int _day;
};
 
class A
{};
 
class B
{
private:
  int a;
  char b;
};
 
class C
{
public:
  void D(){}
};
 
int main()
{
  Data d1;
 
  cout << sizeof(d1) << endl;
  cout << sizeof(A) << endl;
  cout << sizeof(B) << endl;
  cout << sizeof(C) << endl;
}

1.怎么计算类对象的大小呢?(Data类,类A、B、C)它们的大小分别为多少?

一个类的大小,实际就是该类中”成员变量”之和

①为什么不算成员函数?

实例化的不同对象调用函数是同一个函数指针,同一个地址,每个对象都放一份,会大大浪费

2.空类类对象大小为1

当然要注意内存对齐注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

控制台输出显示

结构体内存对齐

详细讲解请看该篇文章:


this指针


this指针的引入

看一下前面提到的Data(日期类)

class Data
{
public:
  void Init(int yaer, int month, int day)
  {
    _year = yaer;
    _month = month;
    _day = day;
  }
 
  void Print()
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
 
private:
  int _year;
  int _month;
  int _day;
};
 
int main()
{
  Data d1;
  Data d2;
 
  d1.Init(2024, 4, 27);
  d2.Init(2023, 4, 27);
 
  d1.Print();
  d2.Print();
 
  return 0;
}

现有这样一个问题,

当d1调用Init成员函数时,该函数如何知道应该设置d1对象而不是设置d2对象呢?

C++中通过引入this指针解决该问题,

即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象)

函数体中所有“成员变量”的操作,都是通过该指针去访问

只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成

this指针的特性

1、this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。

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

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

4、this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传

递,不需要用户传递


d1和d2都调用该成员函数,但结果却不同的原因是

void Print() 函数再编译器编译是 void Print(Data* const this),有一个隐藏参数this指针

d1调用函数时,是这样子来访问的:

&d1->_year、&d1->_month、&d1->_day,

此时this指针就是d1的指针,&d1是实参,Data* this是形参(局部变量),访问的是d1的对象_year,_monh,_day


C语言和C++实现Stack的对比


C语言

共性

1、每个函数的第一个参数都是Stack*

2、函数中必须要对第一个参数检测,因为该参数可能会为NULL函数中都是通过Stack*参数操作栈的

3、调用时必须传递Stack结构体变量的地址

结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据的方式是分离开的,而且实现上相当复杂一点,涉及到大量指针操作,稍不注意可能就会出错。


C++

C++中通过类可以将数据 以及 操作数据的方法进行完美结合

通过访问权限可以控制那些方法在类外可以被调用,即封装,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。

而且每个方法不需要传递Stack*的参数了,编译器编译之后该参数会自动还原,

即C++中 Stack * 参数是编译器维护的,C语言中需用用户自己维护。

目录
相关文章
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
553 12
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
288 0
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
466 0
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
|
编译器 C++
类和对象(下)C++
本内容主要讲解C++中的初始化列表、类型转换、静态成员、友元、内部类、匿名对象及对象拷贝时的编译器优化。初始化列表用于成员变量定义初始化,尤其对引用、const及无默认构造函数的类类型变量至关重要。类型转换中,`explicit`可禁用隐式转换。静态成员属类而非对象,受访问限定符约束。内部类是独立类,可增强封装性。匿名对象生命周期短,常用于临时场景。编译器会优化对象拷贝以提高效率。最后,鼓励大家通过重复练习提升技能!
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
289 16
|
存储 编译器 C++
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
978 6