C++初阶类与对象(一):学习类与对象、访问限定符、封装、this指针

简介: C++初阶类与对象(一):学习类与对象、访问限定符、封装、this指针

入门知识已经梳理完毕了,接下来就进入到面型对象的部分学习了


1.面向过程和面向对象初步认识


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

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

将大象和冰箱看作两个对象,每个对象都有自己的特征和行为

首先,冰箱可能有一个开门和关门的方法,而大象可能有一个“装进冰箱”的方法。在这种情况下,你会调用冰箱的“开门”方法,然后调用大象的“装进冰箱”方法,最后再调用冰箱的“关门”方法


2.类的引入


C语言结构体中只能定义变量 。在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数

之前c:

struct Stack
{
  int* a;
  int top;
  int capacity;
  //功能函数只能在外面
};


现在C++:

struct Stack
{
  int* a;
  int top;
  int capacity;
  void Init(size_t capacity)
  {
    //
  }
  void Push(const DataType& data)
  {
    // 扩容
    _array[_size] = data;
    ++_size;
  }
  //............
};

而C++里sturct会用class来代替

C++兼容c语言struct的所有用法,struct同时升级成了类。 注意:

  1. 类名就是类型,Stack就是类型,不需要加struct。甚至链表里定义next指针时也不需要
  2. 类里面可以定义函数


3.类的定义


3.1类的结构


class ClassName {
public:
    // 公有成员函数和变量
    // 可以被类外部访问
private:
    // 私有成员函数和变量
    // 只能被类内部成员函数访问
protected:
    // 保护成员函数和变量
    // 类的继承者可以访问
};

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


3.2类的两种定义方式


3.2.1声明和定义全部放在类体中


需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理(当然这里也是建议,具体还是要看编译器)

class Stack
{
private:
  int* a;//这三个变量都是声明,内存没有给他们分配空间,创建对象后才有空间
  int top;
  int capacity;
public:
  void Init(size_t capacity)//声明和定义都在类内
  {
    a = nullptr;
    top = -1;
    capacity = 0;
  }
  //............
};


3.2.2声明和定义分开


在类内只进行声明,定义在类外

一般:类声明放在.h文件中,成员函数定义放在.cpp文件中

注意:成员函数名前需要加类名 : :(感觉跟命名空间一个用法)

class Stack
{
private:
  int* a;
  int top;
  int capacity;
public:
  void Init(size_t capacity);//类内声明
  //............
};
void Stack::Init(size_t capacity)//类外定义   注意加上  类名::
{
  a = nullptr;
  top = -1;
  capacity = 0;
}


3.3成员变量命名规则的建议


有时候会有这种情况:



class Date
{
private:
  int year;
  int mouth;
  int day;
public:
  void Init(int year, int mouth, int dat)
  {
    year = year;//我们大多时会习惯于相同变量名,现在就遇到疑惑了
    //......
  }
};

为了避免上述情况,我们习惯于在成员变量前加_(大家默许的规定吧)

int _year;

int _mouth;


4.类的访问限定符及封装


在上面的代码里大家看到了private、public之类的,现在就来讲它们:


4.1访问限定符


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

访问限定符说明:


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

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

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

如果后面没有访问限定符,作用域就到 } 即类结束。

class的默认访问权限为private,struct为public(因为struct要兼容C,C在struct外都能访问)

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



4.2封装


我们大家都知道面向对象的三大性质:封装、继承、多态

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

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

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


5.类的作用域


类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域

class Date
{
private:
  int _year;
  int _mouth;
  int _day;
public:
  void Init(int year, int mouth, int day);
};
void Date::Init(int year, int mouth, int day)
{
  _year = year;
}


6.类的实例化


用类类型创建对象的过程,称为类的实例化(类 和 对象是 1对多的关系)


类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个类,来描述具体学生信息

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

类和对象就像是房子和蓝图的关系

蓝图没有空间,你怎么能放数据进去呢?

所以需要实例化后,再进行赋值等一系列操作


7.类对象模型


7.1类对象的存储方式


只保存成员变量,成员函数存放在公共的代码段(成员函数的地址不在对象中,成员变量是在的)


结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐

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


7.2结构体内存对齐规则


第一个成员在与结构体偏移量为0的地址处。

其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

注意:对齐数 = 编译器默认的一个对齐数与该成员大小的较小值 (VS中默认的对齐数为8)

结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。

如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍


8.this指针


8.1this指针的引出


class Date
{
public:
  void InitDate(int year, int month, int day)
  {
    _year = year;
    _month = month;
    _day = day;
  }
  void PrintDate()
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date d1;
  Date d2;
  d1.InitDate(2024, 1, 2);
  d2.InitDate(2023, 1, 2);
  d1.PrintDate();
  d2.PrintDate();
  return 0;
}

有一个问题:Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象


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

所以实际上:


8.2 this指针的特性


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

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

this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给

this形参。所以对象中不存储this指针。

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

递,不需要用户传递


class Date
{
public:
  // 不能显示的写实参和形参
  // void Print(Date* const this),这样不行
  void Print()
  {
    //this = nullptr;  this实际上穿过来了
    cout << this << endl;
    // 但是可以在类里面显示的使用
    cout << this->_year << "-" << this->_month << "-" << this->_day << endl;//写出来也没事
    cout << _year << "-" << _month << "-" << _day << endl;//正常是这样,不用写
  }
private:
  int _year=1;     // 年
  int _month=1;    // 月
  int _day=1;      // 日
};


8.3经典问题


class A
{
public:
  void print()
  {
    cout << "print()";
  }
private:
  int _a;
};
int main()
{
  A* a = nullptr;//现在是空指针
  a->print();
  return 0;
}

可以运行成功:实际上没有对a进行解引用操作和指向空间的访问

class A
{
public:
  void print()
  {
    cout << "print()" << endl;
    cout << _a;//多了这个,对a指向的空间访问
  }
private:
  int _a;
};
int main()
{
  A* a = nullptr;//现在是空指针
  a->print();
  return 0;
}


今天就到这里了,下次给大家详细介绍构造函数和析构函数,感谢支持!!!

目录
相关文章
|
3月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
74 0
|
13天前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
1月前
|
Java 编译器 C++
c++学习,和友元函数
本文讨论了C++中的友元函数、继承规则、运算符重载以及内存管理的重要性,并提到了指针在C++中的强大功能和使用时需要注意的问题。
17 1
|
2月前
|
数据安全/隐私保护 C语言 C++
C++(七)封装
本文档详细介绍了C++封装的概念及其应用。封装通过权限控制对外提供接口并隐藏内部数据,增强代码的安全性和可维护性。文档首先解释了`class`中的权限修饰符(`public`、`private`、`protected`)的作用,并通过示例展示了如何使用封装实现栈结构。接着介绍了构造器和析构器的使用方法,包括初始化列表的引入以及它们在内存管理和对象生命周期中的重要性。最后,通过分文件编程的方式展示了如何将类定义和实现分离,提高代码的模块化和复用性。
|
4月前
|
存储 C++ 容器
【C++】开散列实现unordered_map与unordered_set的封装
【C++】开散列实现unordered_map与unordered_set的封装
48 0
|
21天前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
21 4
|
21天前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
19 4
|
21天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
17 1
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
1月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)