首页 > 编程语言 >【C++入门】类和对象(一) 详解(class、struct、类的定义、类的实例化、类的封装、对象、this指针)

【C++入门】类和对象(一) 详解(class、struct、类的定义、类的实例化、类的封装、对象、this指针)

时间:2023-04-23 19:36:17浏览次数:42  
标签:函数 int 成员 C++ void 对齐 class struct

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

面向过程更加关注解决问题的过程和步骤。

而面向对象关注的是对象,对一个事情拆分成不同的对象,靠对象之间的交互实现。

例如:外卖系统

面向过程:

上架→点餐→派单→送餐

注重过程步骤

面向对象: (更加贴近现实之间的业务逻辑交互)

骑手→商家←用户

注重对象和对象之间的交互

对面向对象编程方式的看法:

面向对象是面向过程更高阶的开发方式,是语言进化方向。在工程里面更加的使用广泛。

2.类的引入

struct 在c语言里面定义结构体,但是在c++里面升级成了类,在c++中如下定义都是合理的

struct Stack
{
  //成员变量
  int* a;
  int top;
  int capacity;
  
}
int main()
{
   struct Stack st1;

   Stack st2;
}

类里面定义的变量是成员变量,定义实现的函数是成员函数

下面简单实现一个类

#include<iostream>
struct Stack
{
  //成员函数
  void Init(int defaultCapacity =4)
  {
    (int*)a=(int*)malloc(sizeof(int)*capacity);
    if(a==nullptr)
    {
      perror("malloc 申请空间失败");
      return;
    }
    capacity=defaultCapacity;
    top=0;
  }
  
  void Push(int x)
  {
    if(top==capacity)
    {
      //扩容
    }
    a[top++]=x;
  }
  
  
  void Destroy()
  {
    free(a);
    a=nullptr;
    top=capacity=0;
  }

  //成员变量
  int* a;
  int top;
  int capacity;
  
}
int main()
{
    struct Stack st1;
    st1.Init(20);
    Stack st2;
    st2.Init();
    st2.Push(1);
    st2.Push(2);
    st2.Push(3);
    st2.Push(4);
    st2.Destroy();
  
  return 0;
}

上面结构体的定义,在c++中更喜欢用class来定义类。

3.类的定义

关键字:class

class Student
{
  ... //成员变量 成员方法
};    //一定要注意class和struct 定义的时候一样花括号后面要加分号

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。

类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。

类的两种定义方式:

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

如图:

【C++入门】类和对象(一) 详解(class、struct、类的定义、类的实例化、类的封装、对象、this指针)_对象【C++入门】类和对象(一) 详解(class、struct、类的定义、类的实例化、类的封装、对象、this指针)_类_02


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

【C++入门】类和对象(一) 详解(class、struct、类的定义、类的实例化、类的封装、对象、this指针)_this指针_03

4.1访问限定符说明:

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

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

class Student
{
  public:
   int Getage()
    {
	    return _age;
    } 
   string Getname()
    {
	    return _name;
    }
  int Gethight()
    {
	    return _hight;
    }
  string Getsex()
    {
	    return _sex;
    }
  
private:
	//成员变量
	int _age;
	string _name;
	int  _hight;
	string _sex;  
};

C++中struct和class的区别是什么?

解答:C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来

定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类

默认访问权限是private。注意:在继承和模板参数列表位置,struct和class也有区别


4.2封装

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

4.2.1什么是封装?

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

封装本质是一种管理,对类进行管理,把类的变量、函数进行管理。让用户更加方便使用类。

理解:可以理解成我在用的电脑,电脑就是被封装好的类,电脑里面的cpu,精密仪器,就是被类管理好的变量、函数。

总结:

在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来

隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。

【C++入门】类和对象(一) 详解(class、struct、类的定义、类的实例化、类的封装、对象、this指针)_对象_04

5.类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 ::

作用域操作符指明成员属于哪个类域。

class Person
{
public:
    char _name[20];
    char _gender[3];
    int _age;
};
 
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
 cout << _name << " "<< _gender << " " << _age << endl;
}

作用域使用范围:我们可以在类外,通过 :: 指定访问类公共域内的变量和函数,不能访问private私有域内的变量和函数。

6.类的实例化

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

理解:

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

有分配实际的内存空间来存储它;

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

3.类可以理解为一个模型或者一个造房子的图纸。而对象是通过模型算出的数据或者通过图纸造出的房间、窗户等。实例化的东西。

【C++入门】类和对象(一) 详解(class、struct、类的定义、类的实例化、类的封装、对象、this指针)_对象_05

代码实现实例化

【C++入门】类和对象(一) 详解(class、struct、类的定义、类的实例化、类的封装、对象、this指针)_成员变量_06

7.类的对象大小的计算

7.1 如何计算类对象的大小

class A
{
    public:
    void PrintA()
    {
        cout<<_a<<endl;
    }
private:
    char _a;
};

问题:类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算

一个类的大小?

类的对象包括里面可以访问的成员函数或者成员变量,和不可以访问的成员变量或者成员函数。

计算类的大小,需要用到内存对齐的知识,我们在计算的时候是跳过类里面的函数,不进行计算,只计算成员变量内存对齐之后的大小。

7.2计算实例

// 类中既有成员变量,又有成员函数
class A1 {
public:
    void f1(){}
private:
    int _a;
};
// 类中仅有成员函数
class A2 {
public:
   void f2() {}
};
// 类中什么都没有---空类
class A3
{};

计算出 sizeof(A1):_____ (1) sizeof(A2)______(1)        sizeof(A3)_________(1)

(空类或者类无成员变量的内存为1)

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

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

7.3 结构体内存对齐规则

  • 第一个成员在与结构体偏移量为0的地址处。
  • 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
  • 注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
  • VS中默认的对齐数为8
  • 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
  • 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
  • 5.当设置的内存对齐大小过小,那么类在机器上读取数据次数增加,时间增多,效率降低,但是空间利用率高。
  • 6.当设置的内存对齐大小过大,那么类在机器上读取数据次数少,同样的读取速度快,效率高,但是空间出现浪费的问题。
  • 7.那么我们如何设置这个内存对齐数,取决于机器读取的对齐数大小,选取合适的大小进行读取类的数据。

8.类成员函数的this指针

8.1 this指针的引出

class Date
{ 
public:
 void Init(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 void Print()
 {
 cout <<_year<< "-" <<_month << "-"<< _day <<endl;
 }
private:
 int _year;     // 年
 int _month;    // 月
 int _day;      // 日
};
int main()
{
 Date d1, d2;
 d1.Init(2022,1,11);
 d2.Init(2022, 1, 12);
 d1.Print();
 d2.Print();
 return 0;
}
  • c++中是如何知道我调用d1.Print()和d2.Print(),分别打印出不同的内容呢?

我们通过查看反汇编看的出,调用的函数call命令地址是一样的,说明调用的函数是同一个函数,然后那么推断出是参数传递不同,那么在C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

8.2this指针的特性

this指针的类型:类类型 *const,那么this指针不能被成员函数赋值

使用范围: 只在成员函数内部使用

this指针存储的位置: this指针本质上是“成员函数”的形参,当d1.Print()调用成员函数的时候,对象的地址传给了成员函数的this形参,所以对象中不存储this指针,this指针是存放在栈里面,被编译器通过ecx寄存器自动进行传递

对比:

这是我们写的,如下

void Print()
 {
 cout <<_year<< "-" <<_month << "-"<< _day <<endl;
 }

这个编译帮我们传递的形参,真实的调用

void Print(Date* this)
 {
 cout <<this->_year<< "-" <<this->_month << "-"<< this->_day <<endl;
 }

this指针可以为空?

#include<iostream>
#include<string>
using namespace std;
class person
{
public:
	person()
	{}
	person(string a = "", int b = 12)
	{
		name = a;
		age = b;

	}
	void test()
	{
		cout << "这是test" << endl;
	}
	void change()
	{
		cout << "这是change" << endl;
		cout << name << endl;
	}
	~person()
	{}
private:
	string name;
	int age;
};
//
void test()
{
	person A("张三", 13);
	person B("李四", 15);
	person* p = NULL;
	A.test();
	A.change();
	p->test();
	p->change();
}

int main()
{
	test();
}

【C++入门】类和对象(一) 详解(class、struct、类的定义、类的实例化、类的封装、对象、this指针)_this指针_07

  • 当把空的this传入 test(NULL)时: 然后 test没有调用任何函数,所以执行函数。
  • 当把空的this传入 change(NULL)时: 然后 change通过this指针调用this->name,而this是空的,所以出错了。

8.3 c++中stack的实现

typedef int DataType;
class Stack
{
public:
 void Init()
 {
 _array = (DataType*)malloc(sizeof(DataType) * 3);
 if (NULL == _array)
 {
 perror("malloc申请空间失败!!!");
 return;
 }
 _capacity = 3;
 _size = 0;
 }
 void Push(DataType data)
 {
 CheckCapacity();
 _array[_size] = data;
 _size++;
 }
 void Pop()
 {
 if (Empty())
 return;
 _size--;
 }
 DataType Top(){ return _array[_size - 1];}
 int Empty() { return 0 == _size;}
 int Size(){ return _size;}
 void Destroy()
 {
 if (_array)
 {
 free(_array);
 _array = NULL;
 _capacity = 0;
 _size = 0;
 }
 }
private:
 void CheckCapacity()
 {
 if (_size == _capacity)
 {
 int newcapacity = _capacity * 2;
 DataType* temp = (DataType*)realloc(_array, newcapacity *
sizeof(DataType));
 if (temp == NULL)
 {
 perror("realloc申请空间失败!!!");
 return;
 }
 _array = temp;
 _capacity = newcapacity;
 }
 }
private:
 DataType* _array;
 int _capacity;
 int _size;
};
int main()
{
 Stack s;
 s.Init();
 s.Push(1);
 s.Push(2);
 s.Push(3);
 s.Push(4);
 
 printf("%d\n", s.Top());
 printf("%d\n", s.Size());
 s.Pop();
 s.Pop();
 printf("%d\n", s.Top());
 printf("%d\n", s.Size());
 s.Destroy();
 return 0;
}

C++中通过类可以将数据以及操作数据的方法进行完美结合,通过访问权限可以控制那些方法在类外可以被调用。C++中把参数传递交给了编译器,不用程序员来维护,而c语言实现一个stack,在主函数里面调用函数需要自己主动维护参数的传递。

标签:函数,int,成员,C++,void,对齐,class,struct
From: https://blog.51cto.com/u_15831056/6218472

相关文章

  • C++数据结构(栈)
    栈是一种受限的线性表,将允许插入和删除的操作的一端称为栈顶,另一端称之为栈底,向栈中插入元素叫入栈,删除元素叫出栈。栈被称为是后进先出的线性表(LIFO)顺序栈顺序存储,即使用一段连续内存空间依次存储栈中数据。这里通过一维数组动态分配内存的方式保存数据定义代码如下:#defi......
  • 纯c++删除自身目录,和该目录下的所有内容______以及创建文件夹
    头文件.h#ifndefAUTODELETEADDFOLDER_H#defineAUTODELETEADDFOLDER_H#include<unistd.h>#include<stdlib.h>#include<errno.h>#include<dirent.h>#include<string.h>#include<iostream>#include<sys/stat.h>#inclu......
  • 在Go中map[]bool与map[]struct{}性能对比
    在Go中,map[]struct{}在性能和内存消耗方面比map[]bool更好,时间上快了5%,内存消耗少了10%,尤其是在处理大型集合时。众所周知,Go语言没有内置Set,因此开发人员使用map来模仿Set的行为。使用map来实现Set意味着map的值不重要,我们只需要关注键的存在。大多数情况下,人们可能会选择bool,因为......
  • mybatis-plus没有将XML配置文件放到classpath路径下的解决办法
    1.需求:我将mapper接口对应的xml文件没有放到resources路径下,而是放到了如下图中,导致无法识别1.1默认可以放mapper对应配置文件的位置1.2本人实际放置的mapper对应的xml文件位置2.解决:2.1第一步:在pom文件中添加如下配置<build><resources><reso......
  • 解决 Visual C++ 17.5 __cplusplus 始终为 199711L 的问题
    00.软件环境VisualStudio2022,VisualC++,Version17.5.401.问题描述在应用https://github.com/ToniLipponen/cpp-sqlite的过程中,发现源代码文件sqlite.hpp中,有一处宏,和本项目的C++LanguageStandard有关,如下图所示:将鼠标悬停在__cplusplus这个宏上,可以看到它......
  • 04-23: dataclasses使用方法
    vehicle_seeds:List[int]=dataclasses.field(default_factory=list)dataclasses模块提供了一种简洁的方式来定义Python类在上面的代码中,使用dataclasses.field()函数为vehicle_seeds提供了一个默认工厂函数,该函数用于生成一个空的整数列表,即当vehicle_seeds没有被指......
  • 【c&c++】std::string::npos的使用
    std::string::nposstd::string::npos是一个常数,它等于size_type类型可以表示的最大值,用来表示一个不存在的位置,类型一般是std::container_type::size_type。定义staticconstsize_typenpos=-1;#include<iostream>intmain(intargc,char*argv[]){size_ta=-1......
  • jquery validate 例子延伸--如何使用classname而不是name来验证
    可用例子一:底下内容摘自链接:http://jsfiddle.net/Nbcj9/ 可用例子二:底下内容摘自:http://jsfiddle.net/rq5ra/1/<formid="myform"><inputtype="text"name="field_1"class="num"/><br/><inputtype="te......
  • 第14届蓝桥杯C++B组省赛题解(更新中)
    目录A.日期统计题目内容思路代码答案B.01串的熵题目内容思路代码答案C.冶炼金属题目内容输入格式输出格式输入样例输出样例思路代码A.日期统计题目内容小蓝现在有一个长度为100的数组,数组中的每个元素的值都在0到9的范围之内。数组中的元素从左至右如下所示:5686......
  • C++管理堆上内存
    代码中如果有使用到堆上内存,必然涉及到内存的释放时机问题,有别于python的try...finally语法,C++中要实现类似的语法则显得比较困难,因此需要另辟蹊径,用栈内存的自动释放管理堆内存的释放。思路如下,用一个类包装好堆内存的分配(构造)和释放(析构),包装类在函数中调用时均为栈......