c++初阶-------类和对象-1
https://developer.aliyun.com/article/1499042
类的存储方式的猜测
我们假设一下:
1.每创建一个对象,都会保存对应成员的所有代码(我们可以想象就是为类函数和成员变量都开辟空间)
这样给内存造成了很大的浪费,
2. 每创建一个对象,都会保存类的成员变量的空间和类的方法的地址(简单的理解就是开辟成员变量的空间和存储对应类的方法的地址)
3. 类只保存成员变量的大小,成员函数存放在公共区域
#include<iostream> #include<stdlib.h> using std::cout; using std::endl; using std::cin; class Ta { public: void Fun() { } private: int _a; }; int main() { Ta var; cout << sizeof(var); return 0; }
这种情况可以说明,类的储存的情况是按照第三种来的,多个对象调用同名类成员函数是同一个函数
类的大小的特殊情况
#include<iostream> #include<stdlib.h> using std::cout; using std::endl; using std::cin; class Ta { }; int main() { Ta var; cout << sizeof(var); return 0; }
#include<iostream> #include<stdlib.h> using std::cout; using std::endl; using std::cin; class Ta { public: void Fun() { } }; int main() { Ta var; cout << sizeof(var); return 0; }
这里涉及的知识有点多,后面讲解
准确的说,成员变量存在对象里面,其他的成员不是
this 指针
上面可能就会有疑问,不同的对象的成员变量不同,但是成员函数是相同的,那这个成员函数怎么区分这些成员变量是来自哪个对象的呢
#include<iostream> #include<stdlib.h> using std::cout; using std::endl; using std::cin; class Ta { public: void Fun() { cout << _a << endl; } void Fun(Ta * _this) { cout << _this->_a << endl; } public: int _a; }; int main() { Ta var; cout << sizeof(var)<<endl; var._a = 10; var.Fun(); var.Fun(&var); return 0; }
再结合我们之前的C语言写一个栈的时候,要传一个结构体的地址,而在c++可以不用,是因为c++编译器做了很多的步骤,就是解决了传结构体的地址的这个步骤
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏
的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”
的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编
译器自动完成
this的介绍
上面我们知道,类的成员函数都有一个隐藏的this指针
头文件:
include<iostream> using std::cout; using std::endl; using std::cin; class Data { public: void Print() { cout << _year << "-" << _month << "-" << _day << endl; cout <<this-> _year << "-" << this->_month << "-" << this->_day << endl; } void priyear(); void Init(int year, int month, int day); private: int _year; int _month; int _day; };
cpp文件
void Data::priyear() { cout << this->_year << endl; } void Data::Init(int year, int month, int day) { this->_year = year; this->_month = month; this->_day = day; } int main() { Data time1; Data time2; time1.Init(2024,1,1); time2.Init(2024, 1, 2); time1.Print(); time2.Print(); return 0; }
一般我们不会写出this这个指针,新手可以写出来熟悉一下
注意一下: this指针不是存在对象里面的,(前面我们计算类的大小,只包括成员变量,不包括this指针,所以可以说 this指针不是存在对象里面的)
准确的说this就是一个形参,形参是存放在栈帧上面的,所以this存在于栈帧
小练习:
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行 class A { public: void PrintA() { cout<<"print()"<<endl; } private: int _a; }; int main() { A* p = nullptr; p->PrintA(); return 0; }
这道题正常运行,前面我们学习了类的大小,知道类的成员函数是存储在公共区域的,并且函数地址也不在类中,调用类的成员函数,是在链接的符号表进行寻找的,然后找到对应的地址去进行运行,
写这个"p->PrintA();"是为了表明这个函数出自A这个结构体,而实际不存在类中
在字符表找到地址就是红框里面的
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行 class A { public: void PrintA() { cout<<_a<<endl; } private: int _a; }; int main() { A* p = nullptr; p->PrintA(); return 0; }
这个会报错,因为this是空指针,成员变量存放在类里面,需要通过类的指针进行访问
C语言和C++实现Stack的对比
我们在C语言中,我们使用结构体成员,我们要一般通过写一个函数传入该结构体变量的地址进行访问
C语言版
#include<stdio.h> #include<stdlib.h> struct Stack { int* arr; int top;//栈顶元素的下一个 int capacity; }; void Init(struct Stack* obj) { obj->arr = (int*)malloc(sizeof(int) * 3); obj->top = 0; obj->capacity = 3; } int main() { struct Stack sta; Init(&sta); return 0; }
结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据的方式是分离开的,而且实现上相当复杂一点,涉及到大量指针操作,稍不注意可能就会出错。
c++版
#include<iostream> using std::cout; using std::endl; using std::cin; class Stack { public: void Init() { arr = (int*)malloc(sizeof(int) * 3); top = 0; capacity = 3; } private: int* arr; int top;//栈顶元素的下一个 int capacity; }; int main() { Stack sta; sta.Init(); return 0; }
c++这里不用传结构体变量的指针,因为编译器帮我们搞定了
C++中通过类可以将数据 以及 操作数据的方法进行完美结合,通过访问权限可以控制那些方法在
类外可以被调用,即封装
C++中 Stack * 参数是编译器维护的,C语言中需用用户自己维护。