易语言网站开发教程,千博企业网站系统,重庆网站制作天,打开网站弹出qq对话框文章目录 类类定义格式访问限定符类域 实例化实例化概念对象大小 this指针两道nt题目题目一题目二 C和C语言实现stack对比 类
类定义格式
新增一个关键字class#xff0c;后加上类的名字#xff0c;{}中为类的主体#xff0c;类中的函数称为类的⽅法或者成员函数定义在类⾯… 文章目录 类类定义格式访问限定符类域 实例化实例化概念对象大小 this指针两道nt题目题目一题目二 C和C语言实现stack对比 类
类定义格式
新增一个关键字class后加上类的名字{}中为类的主体类中的函数称为类的⽅法或者成员函数定义在类⾯的成员函数默认为inline
访问限定符
C⼀种实现封装的⽅式⽤类将对象的属性与⽅法结合在⼀块让对象更加完善通过访问权限选择性的将其接⼝提供给外部的⽤⼾使⽤有三种访问限定符public修饰的成员在类外可以直接被访问protected和private修饰的成员在类外不能直接被访问protected和private是⼀样的以后继承章节才能体现出他们的区别访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌如果后⾯没有访问限定符作⽤域就到}即类结束⼀般成员变量都会被限制为private/protected需要给别⼈使⽤的成员函数会放为public 酱酱到这里我们就可以看懂下面的代码啦
#includeiostream
#includeassert.h
using namespace std;
class Stack
{
public: // 共有就是说客户可以使用咱们定义的方法void Init(int capacity 4) // 感觉类让函数的实现更加方便这里不需要写函数的参数因为就是通过这个类型的变量来调用的这个方法{_array (int*)malloc(sizeof(int) * capacity);if (nullptr _array){perror(malloc申请空间失败);exit(1);}_capacity capacity;_top 0;}void Push(int x){_array[_top] x;}int Top(){assert(_array);return _array[_top - 1];}void Destroy(){free(top 0);_array nullptr;_top _capacity 0;}private: // 私有就是说客户不能使用不能改变的东西//成员变量int* _array;size_t _capacity;size_t _top;
};int main()
{Stack st; // 类名就是对象变量类型st.Init(); // 和结构体一样通过.操作符来访问类里的元素这里就是类里的方法st.Push(1);st.Push(2);cout st.Top() endl;st.Destroy();
}以上就是类的一个基本用法 C中struct也可以定义类C兼容C中struct的⽤法同时struct升级成了类明显的变化是struct中可以定义函数⼀般情况下我们还是推荐⽤class定义类
struct Person
{
public:void Init(const char* name, int age, int tel){strcpy(_name, name);_age age;_tel tel;}void Print(){cout 姓名 _name endl;cout 年龄 _age endl;cout 电话 _tel endl;}private:char _name[10];int _age;int _tel;
};int main()
{Person p;p.Init(zhangsan, 18, 13409873);p.Print();
}class定义成员没有被访问限定符修饰时默认为privatestruct默认为public
类域
类定义了⼀个新的作⽤域类的所有成员都在类的作⽤域中在类体外定义成员时(声明和定义分离)需要使⽤::作⽤域操作符指明成员属于哪个类域
实例化
实例化概念
⽤类类型在物理内存中创建对象的过程称为类实例化出对象类是对象进⾏⼀种抽象描述是⼀个模型⼀样的东西限定了类有哪些成员变量这些成员变量只是声明没有分配空间⽤类实例化出对象时才会分配空间 我们看下面代码
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()
{//Data::_year 1; //err 这里的对象是类的一部分Date d1; // 类实例化出对象Date d2;d1.Init(2024, 3, 31);d1.Print();d2.Init(2024, 7, 5);d2.Print();return 0;
}对象大小
首先我们思考的问题就是在计算类的大小的是时候要不要加上函数地址。结论是不需要加只需要考虑内存对齐的问题为什么呢 我们想如果两个对象都调用类里的同一个成员函数这两个成员函数的地址是一样的我们没有必要创建一个变量就申请一片空间来存放函数的地址这样会造成冗余我们只用把这些方法都放在一个公共的地方使用的时候直接用就行 而成员变量不一样每一个对象的成员变量都不相同所以每一个对象都只用为成员变量开辟空间 下面我们复习一下对齐规则
第⼀个成员在与结构体偏移量为0的地址处其他成员变量要对⻬到某个数字对⻬数的整数倍的地址处注意对⻬数编译器默认的⼀个对⻬数与该成员⼤⼩的较⼩值VS中默认的对⻬数为8结构体总⼤⼩为最⼤对⻬数所有变量类型最⼤者与默认对⻬参数取最⼩的整数倍 为啥要内存对齐 答为了提高访问效率 看下面的实例 假设变量在内存中存储不对齐也就是图中下面的存储方式我们想要访问_i就需要访问两次因为计算机读取数据时是在一个整数倍读取的比方说三十二位机器一次就是读取四个字节的数据且只能在4的整数倍位置读取数据那么按照图中下面的存储方式就要访问两次而按照内存对齐的方式存储只需要访问一次就可以读取到_i 我们再看一个实例 class A
{
public:void print(){}private:char _ch;int _i;
};class B
{void print(){}
};class C
{};int main()
{cout sizeof(A) endl; // 8cout sizeof(B) endl; // 1cout sizeof(C) endl; // 1 return 0;
}对于没有成员变量的类默认开一个字节的空间起占位作用不存储有效数据表示对象存在
this指针
我们再返回去看一下Data类现在就有一个疑问了d1调⽤Init和Print函数时该函数是如何知道应该访问的是d1对象还是d2对象呢那么这⾥就要看到C给了⼀个隐含的this指针解决这⾥的问题编译器编译后类的成员函数默认都会在形参第⼀个位置增加⼀个当前类类型的指针叫做this指针。⽐如Date类的Init的真实原型为void Init(Date* const this, int year, int month, int day) 所以调用的时候实际上是这样
d1.Iint(d1, 2024,3, 31);只不过指针是被省略掉的所以实参的取地址也省略
类的成员函数中访问成员变量本质都是通过this指针访问的如Init函数中给_year赋值this-_year year;C规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理)但是可以在函数体内显⽰使⽤this指针
class Date
{
public: //不可以显示的加// void Init(Date* const this, int year, int month, int day)void Init(int year, int month, int day){// 编译报错error C2106 : “ ” :左操作数必须为左值// this nullptr;// this-_year year;_year year;this-_month month;this-_day day;}void Print(){cout _year / _month / _day endl;cout this-_year / this-_month / this-_day endl;//两种都可以}
private:// 这⾥只是声明没有开空间int _year;int _month;int _day;
};
int main()
{// Date类实例化出对象d1和d2Date d1;Date d2;// d1.Init(d1, 2024, 3, 31);d1.Init(2024, 3, 31);d1.Print();d2.Init(2024, 7, 5);d2.Print();return 0;
}一个小问题this指针存在于内存的哪个区域呢 答案是存在于栈区因为this是一个隐含的形参形参就是函数的参数函数参数存在于栈区
两道nt题目
题目一
class A
{
public:void Print(){cout A::Print() endl;}
private:int _a;
};
int main()
{A* p nullptr;p-Print();return 0;
}A编译报错 B运行崩溃 C正常运行 这个题答案选C 解析A选项编译报错说明是语法错误首先排除p虽然是一个空指针且调用了函数Print但是成员函数的地址没有存在于*p中并且Print方法没有访问成员变量所以不存在解引用所以正常运行
题目二
class A
{
public:void Print(){cout A::Print() endl;cout _a endl;}
private:int _a;
};
int main()
{A* p nullptr;p-Print();return 0;
}结果是运行崩溃 解析cout this-_a endl相当于这样出现了解引用所以程序崩溃
C和C语言实现stack对比
C版
#define _CRT_SECURE_NO_WARNINGS 1
#includestack_c.hvoid STInit(ST* ps)
{assert(ps);ps-a NULL;ps-top 0;ps-capacity 0;
}void STDestroy(ST* ps)
{free(ps-a);ps-a NULL;ps-capacity ps-top 0;
}void STPush(ST* ps, STDataType x)
{assert(ps);if (ps-capacity ps-top){//扩容int newcapacity ps-capacity 0 ? 4 : ps-capacity * 2;STDataType* tmp (STDataType*)realloc(ps-a, newcapacity * sizeof(STDataType));if (tmp NULL){perror(realloc fail);exit(1);}ps-a tmp;ps-capacity newcapacity;}ps-a[ps-top] x;ps-top;
}bool STEmpty(ST* ps)
{assert(ps);return ps-top 0;
}void STPop(ST* ps)
{assert(ps);assert(!STEmpty(ps));ps-top--;
}STDataType STTop(ST* ps)
{assert(ps);assert(!STEmpty(ps));return ps-a[ps-top - 1];
}int main()
{ST st;STInit(st);STPush(st, 1);STPush(st, 2);STPush(st, 3);STPush(st, 4);STPush(st, 5);while (!STEmpty(st)){printf(%d\n, STTop(st));STPop(st);}STDestroy(st);return 0;
}C版
#define _CRT_SECURE_NO_WARNINGS 1
#includeiostream
#includeassert.h
using namespace std;typedef int Datatype;
class stack
{
public:void Init(int n 4){_a (Datatype*)malloc(sizeof(Datatype) * n);if (_a nullptr){perror(malloc fail);exit(1);}_capacity 4;_top 0;}void Push(int x){if (_capacity _top){int newcapacity _capacity * 2;Datatype* tmp (Datatype*)realloc(_a, newcapacity *sizeof(Datatype));if (tmp NULL){perror(realloc fail);return;}_a tmp;_capacity newcapacity;}_a[_top] x;}void Pop(){assert(_top 0);--_top;}bool Empty(){return _top 0;}int Top(){assert(_top 0);return _a[_top - 1];}void Destroy(){free(_a);_a nullptr;_top _capacity 0;}
private:Datatype* _a;int _capacity;int _top;
};int main()
{stack s;s.Init();s.Push(1);s.Push(2);s.Push(3);s.Push(4);while (!s.Empty()){printf(%d\n, s.Top());s.Pop();}s.Destroy();return 0;
}完