主页 > 其他  > 

【哇!C++】类和对象(一)

【哇!C++】类和对象(一)

目录

一、类的引入和定义

1.1 类的引用

1.2 类的定义

二、类的访问限定符及封装

2.1 类的访问限定符

2.2 封装

三、类的作用域

四、对象实例化

五、类对象模型

5.1 类对象的储存方式

5.2 结构体内存对齐规则


一、类的引入和定义 1.1 类的引用

        C语言结构体只能定义变量,在C++中,结构体不仅可以定义变量,也可以定义函数。如下程序所示:

#include<iostream> using namespace std; struct Stack { //成员变量 int* a; int top; int capacity; //成员函数 void Init(int n = 4) { a = (int*)malloc(sizeof(int) * n); if(a == nullptr) { perror("malloc"); return; } } void Push(int x) { a[top++] = x; } } int main() { struct Stack st1;//兼容C Stack st2;//C++用法 st2.top = 0;//对象.成员 st2.Init(); st2.Push(1); st2.Push(2); st2.Push(3); st2.Push(4); return 0; }

         C++兼容C,所以struct Stack st1;是没有问题的,C++的用法为Stack st2;。

1.2 类的定义

        C++中把结构体升级为类,但同时又兼容C中的结构体,为进一步区分,C++引入关键字class替代struct定义类。其中:class为定义类的关键字,ClassName为类的名字,{ }中为类的主体。

class classname { //类体:由成员变量和成员函数组成 //成员变量 //想在类中定义数据,一般情况,变量是private //成员函数 //想在类中实现哪些方法,定义成员函数,一般情况,成员是public }; //注意后边的分号

        C++推荐成员变量名字前加"_",这样可以很方便地区分成员变量和函数参数。例如,我们定义一个时间类:年-月-日,程序如下:

#include<iostream> using namespace std; class Date { void Init(int year, int month, int day) { _year = year; _month = month; _day = day; } int _year; int _month; int _day; } int main() { Date d1; d1.Init(2024, 2, 17); return 0; }

        程序运行后发现调不动,“Date::Init”: 无法访问 private 成员(在“Date”类中声明)。究其原因,在于没有加访问限定符,默认是private,所以把class换成struct,报错就消失了。想在类外直接访问,定义为public;反之定义为private:。

二、类的访问限定符及封装 2.1 类的访问限定符

        C++提出了3种访问限定符:public、protected、private。

public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)。当前阶段可认为protected和private没有区别。

        没有加访问限定符的类,class默认的访问限定是private,struct默认的访问限定是public。程序展示如下:

#include<iostream> using namespace std; 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() { class Date d0; //ok,但不这样用 Date d1; //整体定义,对象实例化 Date::_year; //通过类域不能访问,因为他只是声明,没有空间,就不能访问 d1._year++; //当把_year前的private注释掉以后,ok cout << sizeof(d1) << endl; //12 d1.Init(2025, 2, 17); d1.Print(); return 0; }

        值得注意的是:在Date类private中的_year,_month,_day是定义还是声明? - 声明

        变量的声明和定义的区别:变量的定义是要开空间,即标志是开空间。

2.2 封装

面向对象的三大特性:封装、继承、多态。封装:本质是一种管控:

        1.C++数据和方法都放到类里面,C是分离的;

        2.C++强制加了访问限定符,去对成员进行限制,想给你访问的是public,不想给你访问的是private或protected。

三、类的作用域

        类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用::作用域操作符指明成员属于哪个类域。程序如下:

//Stack.h #include<stdio.h> #include<stdlib.h> #include<iostream> using namespace std; class Stack { private: //成员变量 int* a; int top; int capacity; public: //成员函数 void Init(int n = 4); void Push(int x); }; //Stack.cpp #include"Stack.h" void Stack::Init(int n) { a = (int*)malloc(sizeof(int) * n); if (nullptr == a) { perror("malloc"); return; } capacity = n; top = 0; } void Stack::Push(int x) { a[top++] = x; }

        做类的声明和定义分离的时候需要注意:用之前C的方法定义,会报错,提示是未声明的标识符,即便改成public也不行。

        这是因为,编译器在搜索的时候,默认只在局部域和全局域搜索。在当前的局部没有a、capacity和top,全局也没有。a在Stack.h文件中的Stack这个类里面。在C++中,除了全局域,只要在花括号{ }中括起来的都叫一个域,所以,Stack类形成的新的域,类域,其概念有点像命名空间域。

        所以,语法规定,需要在函数名前加Stack::,就让编译器认为Init和Push不是一个普通函数,而是Stack这个类中的成员函数,此时,a就先在局部域中找,再去类域中去找,最后再去全局域中找。

局部域,就是一个函数,用花括号括起来的。局部域里边还可以再定义一个局部域,例如在for循环中再定义for循环,for循环中定义的内容无法在外部访问;全局域,没有被括起来,整个全局就是全局域;命名空间域,把特定的一些变量圈起来,如果没有指定或没有展开,就不会到命名空间域中搜索。 四、对象实例化

        用类类型创建对象的过程,称为类的实例化。

类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量;做个比方,类实例化对象,有点像用图纸建造房子。类像图纸,类定义了有哪些数据,有哪些方法;房子图纸定义了房子是几室几厅几层。其中,成员函数,就是图纸中的健身房,电梯等公共的设施;成员变量,就是每家每户都需要的卧室、厨房等。

        所以也就解释了Data::_year++;是错误的。用类名直接指定成员访问,就好比,有一张图纸,去图纸中找卧室。所以,必须实例化才能指定访问。

五、类对象模型 5.1 类对象的储存方式

        对象里边存放数据时有两种方案:

        1. 对象里边既存成员变量,也存成员函数的地址。但这种方法会有缺陷:当有d1 d2 d3 d4...时,实例化后,成员变量可以单独存,但是成员函数的地址却都是一样的,存的多份相同的成员函数地址,造成浪费。

        2. 比较好的方式是:每个对象里边只存成员变量,在一个公共的代码区域存储成员函数的地址。如图所示:

        我们通过对下面的不同对象分别获取大小来分析看下:

#include<iostream> using namespace std; //类中仅有成员函数 class A1 { public: void func() { } }; //类中什么都没有---空类 class A2 { } //类中既有成员变量,又有成员函数 class A3 { public: void f1() { } private: int _a; }; int main() { cout << sizeof(A1) <<endl; //1 cout << sizeof(A2) <<endl; //1 cout << sizeof(A3) <<endl; //4 return 0; }

        一个类的大小,实际就是该类中“成员变量”之和,当然要注意内存对齐 。没有成员变量的类对象,大小是1byte,占位,标识对象实例化时定义出来的存在过,所以不是0。

5.2 结构体内存对齐规则

1. 第一个成员在结构体偏移量为0的地址处;

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处;

        注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。         VS中默认的对齐数为8。

3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍;

4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

 

标签:

【哇!C++】类和对象(一)由讯客互联其他栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“【哇!C++】类和对象(一)

上一篇
Java锁代码解析

下一篇
环境变量1