做搜索引擎优化网站费用云南设计网
做搜索引擎优化网站费用,云南设计网,爱奇艺推广联盟,阜南县建设局网站引言
在C语言中#xff0c;我们用结构体来描述一个复杂的对象#xff0c;这个对象可能包括许多的成员#xff0c;如用结构体描述一个学生的成绩#xff0c;或者描述一个日期等。
struct Date
{int _year;int _month;int _day;
};
如上是一个描述日期的结构体定义#x…引言
在C语言中我们用结构体来描述一个复杂的对象这个对象可能包括许多的成员如用结构体描述一个学生的成绩或者描述一个日期等。
struct Date
{int _year;int _month;int _day;
};
如上是一个描述日期的结构体定义里面可以有年、月、日这些成员但是不能在里面有函数的声明或定义这就使得和这个日期对象有关的函数需写在外部在命名时就需要防止冲突。而且C语言的结构体对成员变量的保护不到位可以随意访问对象的成员变量非常不安全。因此C在兼容C语言 struct 的用法的同时将它升级为了类并且C喜欢用 class 关键字来定义类。 类的定义
class 类名
{// 类体由成员函数和成员变量组成}; // 末尾分号不能落下
类体中的内容称为类的成员类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者 成员函数。
struct ListNode
{//struct ListNode* next; // C语言写法ListNode* next; // C可以这样用int val;
};
类名就是类型所以不需要再像C语言一样加上 struct 来表示类型。 接下来我们来定义一个简单的日期类
class Date
{void Init(int year, int month, int day){_year year;_month month;_day day;}int _year;int _month;int _day;
};
我们在类中写了一个 Init 函数用来初始化我们的对象我们试着用这个类来实例化对象。
int main()
{Date d1;d1.Init(2024, 1, 22);return 0;
}
用类的类型创建对象的过程称为类的实例化。可以把类理解为设计图纸用类实例化对象就是用图纸创建出具体的实物。但是目前我们的程序是有问题的这个函数运行不了因为目前这个函数是私有的在类外面无法访问这涉及到访问限定符的问题。 访问限定符
访问限定符包括public公有、private私有、protected保护public 修饰的成员在类外可以直接被访问而 private 和 protected 修饰的成员在类外不能直接被访问现阶段暂时认为private 和 protected 没有什么区别。由于 struct 需要兼容C语言的用法因此类中没使用访问限定符时默认是共有的而 class 默认是私有的因此我们上面那段代码无法运行。
class Date
{
public:void Init(int year, int month, int day){_year year;_month month;_day day;}private:int _year;int _month;int _day;
};
加上访问限定符让成员函数的访问权限为公有就能运行了成员变量一般都定义为私有。值得注意的是如果成员函数在类中定义编译器可能会将其当成内联函数处理因此可以将较短的函数定义在类中而较长的函数可以将声明与定义分离。 如果要将成员函数的声明和定义分离在类外定义函数时需要在函数名字前使用域作用限定符指定类名因为类定义了一个新的作用域。如下所示
class Date
{
public:void Init(int year, int month, int day);private:int _year;int _month;int _day;
};void Date::Init(int year, int month, int day)
{_year year;_month month;_day day;
}
指定类名相当于告诉编译器这个函数是哪个类的不然在涉及成员变量时编译器找不到这个变量的定义。 类的实例化
类是对对象进行描述的就像一张设计图纸限定了类有哪些成员定义出一个类并没有分配实际的内存空间来存储它。在类中定义的成员变量都只是声明没有内存空间。只有当使用这个类去实例化对象的时候它们才会作为该对象的专属成员变量而定义也就是有了空间。
一个类可以实例化出多个对象那它们是不是都有专属于自己的成员变量呢是的来看这个例子
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(2024, 1, 22);d2.Init(2023, 1, 22);d1.print();d2.print();return 0;
} 这里我们用 Date 类实例化了两个对象比对它们进行初始化然后借助 print 函数打印了它们各自的成员变量很明显它们都有各自的成员变量。可是在调用函数时明明没有传参它们的成员变量是怎么被准确找到的呢这就是 this 指针的功劳了。 this 指针
其实每个“非静态的成员函数“的第一个参数都是一个隐藏的 this 指针该指针指向的是当前对象(函数运行时调用该函数的对象)。在函数体中所有涉及“成员”的操作都是通过该指针去访问。只不过这个过程我们看不到因为用户不需要传递该指针编译器会自动帮我们传递与使用该指针。
class Date
{
public:// 第一个形参是帮助理解用户不能自己写// 为了篇幅着想此处 Init 函数只给出声明定义上面有void Init(Date* const this, int year, int month, int day);void print(Date* const this) { // 在函数中显示使用 this 是可以的大部分情况不需要交给编译器cout this-_year this-_month this-_day endl; }private:int _year;int _month;int _day;
};int main()
{Date d1, d2;// 同样第一个实参用于帮助理解用户不能自己传d1.Init(d1, 2024, 1, 22);d1.print(d1);return 0;
}
通过这段代码你应该知道对象的成员变量如何被访问了实际上是编译器在负重前行默默地帮我们传递对象的地址默默地使用 this 指针去访问成员变量。
注意我给出的 this 指针的类型它被 const 所修饰因此不能改变指向也非常合理要是哪位大牛在函数内把 this 指针改了可就不好玩了。
总结一下 this 指针的类型为类类型* const它是“成员函数”第一个隐含的指针形参当对象调用成员函数时自动将对象地址作为实参传递给this形参并且只能在“成员函数”的内部使用一般情况由编译器通过 ecx 寄存器自动传递不需要用户传递。 类对象的大小
int main()
{Date d1;d1.Init(2024, 1, 22);cout sizeof(d1) endl;return 0;
}
依旧是上面的日期类你认为对象 d1 的大小是多少呢通过前面的学习成员变量存储在对象中应该已经是大家的共识了那么成员函数会不会存在对象里呢我们运行下程序看看。
由结果可见成员函数是不存在对象中的。这也很符合实际一个类可以实例化出那么多对象要是每个对象都存一次函数那岂不是太浪费空间了。事实上成员函数存放在公共的代码段大家都能调用。 好了现在你已经知道一个类的大小就是该类中”成员变量”之和并且要注意内存对齐。那么请问 A类的大小又是多少
class A
{};int main()
{cout sizeof(A) endl;return 0;
}
简单没有成员变量所以大小是0是这样吗那么如果我用这个类实例化一个对象你怎么表示这个对象存在过呢简单来说就是你怎么表示这个对象。因此对于没有成员变量的类就算它有成员函数也一样对象中不存储成员函数其实例化对象大小为 1 个字节这个字节不存储有效数据只是来唯一标识这个类的对象。 现在问题又来了this 指针为什么不存在对象中呢首先 this 指针是形参形参一般都是存在栈上的也就是函数栈帧中函数结束它就销毁了。但在 vs 下this 指针通常存储在 ecx 寄存器中因为它需要频繁使用而且本身大小较小将其存放在寄存器中可以提高访问速度。 现在补充一下内存对齐的知识
1. 第一个成员变量存储在结构体偏移量为0的地址即从结构体的起始位置开始。 2. 其他成员变量要对齐到对齐数的整数倍的地址处。 注意对齐数 编译器默认的一个对齐数与 该成员大小的较小值。 VS中默认的对齐数为8 3. 结构体总大小为最大对齐数所有变量类型最大者与默认对齐参数取最小的整数倍。 4. 如果嵌套了结构体的情况嵌套的结构体对齐到自己的最大对齐数的整数倍处结构体的整体大小就是所有最大对齐数含嵌套结构体的对齐数的整数倍。 有趣的程序
class A
{
public:void Print(){cout Print() endl;}private:int _a;
};int main()
{A* p nullptr;p-Print();return 0;
}
该程序可以正常运行虽然 p 是空指针但是并没有涉及到访问空指针的问题因为成员函数不存放在对象中通过 p 能调用函数只是因为 p 指向的对象是 A 类的而 Print 函数也是 A 类的。随后在函数内也没有访问成员变量因此程序没问题。如图所示
这里 p 的作用主要有两个第一让编译器知道调用的是 A 类里的 Print 函数。第二调用函数时顺便把对象的地址也就是 p 本身传给了形参 this 指针。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/88034.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!