当前位置: 首页 > news >正文

C++基础之类、对象一(类的定义,作用域、this指针)

目录

面向对象的编程

类的引入

简介

类的定义

简介

访问限定符

命名规则

封装

简介

类的作用域

类的大小及存储模型

this指针

简介

面向对象的编程

C++与C语言不同,C++是面向对象的编程,那么什么是面向对象的编程呢?

C语言编程,规定了编程的每一步指令,程序从上而下一步一步按照指令,最终达到想要的结果,而面向对象是另一种思路,将一件事情拆分成不同的对象,任务需要依靠对象之间的交互完成,也就是说关注模块和模块之间的关系。

类的引入

简介

类是C++中重要的概念,从C语言的结构体升级而来,C语言的结构体只能定义变量,C++中结构体不仅可以定义变量还能定义函数(成员变量/成员属性,成员函数/成员方法)

typedef int DataType;
struct Stack
{void Init(size_t capacity){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (nullptr == _array){perror("malloc申请空间失败");return;}_capacity = capacity;_size = 0;}void Push(const DataType& data){// 扩容_array[_size] = data;++_size;}DataType Top(){return _array[_size - 1];}void Destroy(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}DataType* _array;size_t _capacity;size_t _size;};int main(){Stack s;s.Init(10);s.Push(1);s.Push(2);s.Push(3);cout << s.Top() << endl;s.Destroy();return 0;}

类的定义

C++中用class作为关键字定义类,其结构如下:

class classname
{//类体//成员变量(成员属性)//成员函数(成员方法)
}; 

访问限定符

C++中设置了访问限定符,其作用是设置类体的属性,访问限定符作用域从当前限定符开始直到下个访问限定符出现结束,如果没有访问限定符则直到类结束;

1、public 修饰的类体,可以直接被外部访问;

2、protected(保护)与private(私有)修饰的类体有同样的特征不允许被外部访问;

3、struct与class在默认的访问限定符不同,struct为了兼容c语言,默认的是public,class默认的是private。

命名规则

先看一下下面代码

class Date
{
private:int year;int month;int day;
public:void init(int year, int month, int day){year = year;month = month;day = day;}void print(){printf("%d-%d-%d\n", year, month, day);}
};
	void init(int year, int month, int day){year = year;month = month;day = day;}

这段代码阅读起来很不方便,形参与类中的成员变量无法区别,为了更好的阅读,在成员变量命名时可以加以区分;

class Date
{
private:int _year;int _month;int _day;
public:void init(int year, int month, int day){_year = year;_month = month;_day = day;}void print(){printf("%d-%d-%d\n", _year, _month, _day);}
};

封装

简介

C++中存在多种特性,面向对象的最主要的是封装,继承,多态;

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

简单的说,封装是一种管理行为,将程序的属性与方法结合在一起,隐藏对象的属性和细节,仅保留对外接口和对象进行交互,封装的特性在C++的类中体现的很明显;

class Date
{
private:int year;int month;int day;
public:void init(int year, int month, int day){year = year;month = month;day = day;}void print(){printf("%d-%d-%d\n", year, month, day);}
};int main()
{Date s;s.print();system("pause");return 0;
}

 上面的代码中,成员变量无法通过外部进行修改,只能通过init函数进行修改;

类的大小及存储模型

先看下面的代码:

class Date
{
public:int _year;int _month;int _day;void init(int year, int month, int day){_year = year;_month = month;_day = day;}void print(){printf("%d-%d-%d\n", _year, _month, _day);}
};int main()
{Date s;printf("类的大小:%d\n", sizeof(s));system("pause");return 0;
}

上面这段代码的结果为12。为什么是12而不是20(包含两个函数的指针),这由类的存储模型决定的。

类的存储模型

 内存通常分为栈区,堆区,静态区,常量区等,(栈区的空间非常小)为了节省空间,编译器会将成员函数放在常量区(代码段)中,使用时寻找函数。

this指针

先看下面的代码及运行结果

class Date
{
private:int _year;int _month;int _day;
public:Date(int year=1, int month=1, int day=1){_year = year;_month = month;_day = day;}void print(){printf("%d-%d-%d\n", _year, _month, _day);}
};int main()
{Date d1(2023,5,7);Date d2(2024, 6, 8);d1.print();d2.print();system("pause");return 0;
}

 成员函数存储在常量区,那为什么d1和d2调用时会打印出不同的结果呢?

这是因为类中的函数有一个隐藏的参数this指针。

void thisprint(Date *this)//this指针隐藏在类的函数中,不能手写,编译器自动完成

那么this指针又是什么呢?

class Date
{
private:int _year;int _month;int _day;
public:Date(int year=1, int month=1, int day=1){_year = year;_month = month;_day = day;}void print(){printf("%d-%d-%d\n", _year, _month, _day);}void thisprint(){printf("this指针:%p\n", this);}
};int main()
{Date d1(2023,5,7);Date d2(2024, 6, 8);printf("d1的地址: %p\n", &d1);d1.thisprint();printf("d2的地址: %p\n", &d2);d2.thisprint();system("pause");return 0;
}

 

 通过上面代码的结果,this指针就是类的地址,那么可以由以下结论;

void print(){printf("%d-%d-%d\n", this->_year, this->_month, this->_day);}
//类能通过常量区的成员函数打印值,是通过指针调用完成的。

 为了保护this指针的值不被修改,this指针会用const修饰,写成const int * this(指针指向的值无法被修改   int * const this指针指向的变量不能被修改);

 this指针也是存在栈区中,其作用域与生命周期随着函数的调用而产生,随着函数的销毁而消失,VS下通常优化在寄存器ecx中;

this 指针可以为空吗?

//先看下这段代码
class A
{
public:void Print(){std::cout << "Print()" << std::endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();system("pause");return 0;
}
//结构体的指针为nullptr,this指针为nullptr,在调用函数时Print函数从常量区中调用,与this指针无关,所以该程序可以正常运行;
//再看下面这段代码
class A
{
public:void PrintA(){std::cout << _a << std::endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->PrintA();return 0;
}
//虽然PrintA函数被成功调用,但是因为p指向nullptr,所以_a是无法读取内存的,因此运行时程序会崩溃

类的作用域

类的作用域是{}中的部分,但是它只是虚拟的,这是因为类类似于图纸;

class Date
{
public:int _year;int _month;int _day;void init(int year, int month, int day){_year = year;_month = month;_day = day;}void print(){printf("%d-%d-%d\n", _year, _month, _day);}
};namespace date
{int _year = 2023;int _month = 5;int _day = 1;
}int main()
{Date s;/*s.print();*/printf("%d-%d-%d\n", date::_year, date::_month, date::_day);printf("%d-%d-%d\n", Date::_year, Date::_month, Date::_day);//该语句无法通过编译system("pause");return 0;
}

 上面的代码中,命名空间的变量可以通过作用域运算符::来找到对应的变量,但是类中定义的变量不能通过,其原因就是类没有被真实创建,类定义的只是图纸。

事实上不光是成员属性不能使用作用域运算符,成员方法也不行,这是因为this指针,虽然类中创建了成员函数,但是类没有被创建,没有this指针,所以运行崩溃。

成员函数的创建

定义成员函数时,如果将成员函数都放在类的内部,那么阅读起来会非常麻烦,通常只在类中声明,在外部定义;

class A
{
public:void PrintA();
private:int _a=10;
};void A:: PrintA()
{std::cout << _a << std::endl;
}int main()
{A _a;_a.PrintA();system("pause");return 0;
}

http://www.lryc.cn/news/65772.html

相关文章:

  • javaScript---设计模式-封装与对象
  • 【消息中间件】kafka高性能设计之内存池
  • 创建型模式——单例(singleton)
  • 算法:迷宫问题
  • 聊聊并发编程的12种业务场景
  • MySQL执行顺序
  • 引领真无线耳机未来趋势,NANK南卡OE骨传导真无线耳机惊艳亮相
  • 5款写作神器,帮助你写出5w+爆款文案,好用到哭
  • 相交链表问题
  • [ubuntu] ax200网卡虚接,导致系统根目录占满而无法进入系统的奇葩问题
  • 本地字体库的引入方法
  • 7种优秀的导航菜单设计总结
  • Problem E. 矩阵游戏 (2023年ccpc河南省赛)
  • 数字孪生模型构建理论及应用
  • Vue面试题:30道含答案和代码示例的练习题
  • 2023-05-09 LeetCode每日一题(有效时间的数目)
  • 第三节课 Linux文件权限
  • 开发STC89C51系列单片机需要的单片机技术
  • 分布式键值存储是什么?(分布式键值存储大值)
  • 多线程(线程同步和互斥+线程安全+条件变量)
  • Flutter学习——开发Flutter需要的技能
  • SPSS如何进行因子分析和主成分分析之案例实训?
  • 图标字体与HTML转义字符:网页设计中的两个关键概念
  • Elasticsearch详解
  • 学习笔记(13)网络基础
  • LeertCode 134 加油站
  • python文件操作的基本流程
  • 1. 两数之和
  • 操作系统:06 进程通信
  • WRF模式