网站建设平台加盟盘多多搜索引擎入口
目录
面向对象的编程
类的引入
简介
类的定义
简介
访问限定符
命名规则
封装
简介
类的作用域
类的大小及存储模型
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;
}