马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
一、类的引入
我们之前已经学过了C语言中的结构体,但是结构体中我们只能界说变量,但是在C++中,结构体内不仅可以界说变量,也可以界说函数。然而对于C++,相比于结构体的界说,更加喜欢用class来代替。
二、类的界说
class classname
{
//类体:由成员函数和成员变量构成
};//不要忘了分号
class为界说类的关键字,classname为类的名字,{}中为类的主体,注意类界说竣事之后背面的分号不可以或许省略。
类体中的内容称为类的成员:类中的变量称为类的属性大概成员变量,类中的函数称为类的方法大概成员函数。
类的两种界说方式:
1.声明和界说全部放在类体中:需要注意的是,成员函数在类中界说,编译器可能将其看成内联函数处理。
- #include<iostream>
- using namespace std;
- class person
- {
- public:
- void showin()
- {
- cout << name << "-" << sex << "-" << age << endl;
- }
- private:
- char* name;
- char* sex;
- int age;
- };
复制代码 2.类声明放在.h头文件中进行声明,成员函数放在.cpp文件中进行界说,需要注意的是,成员函数界说之前需要加上类名::来让编译器知道是该类中的成员函数.
- //.h文件
- #pragma once
- #include<iostream>
- using namespace std;
- class person
- {
- public:
- void showin();
-
- private:
- char* name;
- char* sex;
- int age;
- };
- //.cpp文件
- #include"head.h"
- void person::showin()
- {
- cout << name << "-" << sex << "-" << age << endl;
- }
复制代码 在一样平常的工作中我们更加的倾向于第二种的写法.
再就是我们一样平常的定名规则:我们来看下面一段代码:
- class Date
- {
- public:
- void Init(int year)
- {
- // 这里的year到底是成员变量,还是函数形参?
- year = year;
- }
- private:
- int year;
- };
复制代码 我们在自己写代码的过程中就可能会迷惑于上面的一串代码中的year,究竟哪一个是成员变量中的year,哪一个是函数内部传入的year,很显着,如许的定名时轻易出问题的,以是我们一样平常在定名成员变量的时间会加上一个字符以示区别,就好比我们将成员函数year定名成_year,如许就可以进行有效的区分.
三、类的访问限定符及封装
1.限定访问符
C++实现封装的方式:用类将对象的属性(成员变量)与方法(成员函数)结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
【访问限定符说明】
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是雷同的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果背面没有访问限定符,作用域就到 } 即类竣事。
5. class的默认访问权限为private,struct为public(因为struct要兼容C)
需要注意的是访问限定符只在编译的时间有用,当数据映射到内存后,没有任何访问限定符上的区别.
2.封装
面向对象有三大特性:继续、封装、多态
在类和对象阶段我们主要研究的是其封装特性,封装指的是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
封装本质上是一种管理,让用户更方便使用类。好比:对于电脑如许一个复杂的设备,提供给用
户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和盘算机进行交互,完成日
常事件。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。
四、类的作用域
类界说了一个新的作用域,类的所有成员都在作用域中.在类外界说成员的时间,需要使用::操作符来指明属于哪个类域.
- #include<iostream>
- using namespace std;
- class person
- {
- public:
- void showin();
- private:
- char* _name;
- char* _sex;
- int _age;
- };
- void person::showin()
- {
- cout << _name << "-" << _sex << "-" << _age << endl;
- }
复制代码 在上述的代码中,我们将showin函数放在类外进行界说,于是我们就在类外将showin之前加上person::就可以了.
五、类的实例化
用类范例创建对象的过程,称为类的实例化.
类是对对象进行形貌的,是一个 模型一样的东西,限定了类有哪些成员,界说出一个类并 没
有分配实际的内存空间来存储它. 一个类可以实例化出 多个对象,实例化出的对象占用实际的物理空间,存储类成员变量. - class Person
- {
- public:
- void showin();
- private:
- char* _name;
- char* _sex;
- int age;
- };
- void Person::showin()
- {
- cout << _name << "-" << _sex << "-" << age << endl;
- }
- int main()
- {
- Person._age = 18;//这里会进行报错,"."符号并不能将18存储到_age里面。
- return 0;
- }
复制代码 在上面的代码中我们就可以看到是我们直接使用了Person类中的_age,但是编译器报错了,说明类在进行实例化之前的类本身是不占用存储空间的.
打个实际的比方,在实际中我们在建造房子之前会制作一个筹划图,通过筹划图我们就可以或许更好的制作房子和划分区域,但是我们在房子制作之前,我们只有一张筹划图的,而筹划图并不占用空间.
六、类对象模型
1.怎样盘算类的大小
- class A
- {
- public:
- void PrintA()
- {
- cout << _a << endl;
- }
- private:
- char _a;
- };
复制代码 我们都知道类里面不仅有成员函数也有成员变量,两者都有那我们该怎样来盘算类的大小呢?
实际上我们经过推算可以得到一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐注意空类的大小,空类比力特别,编译器给了空类一个字节来唯一标识这个类的对象.
2.结构体内存对齐规则
1. 第一个成员在与结构体偏移量为0的地点处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地点处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的对齐数为8
3. 结构体总大小为:最大对齐数(所有变量范例最大者与默认对齐参数取最小)的整数倍。
4. 如果嵌套了结构体的环境,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
七、this指针
- 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(2022, 1, 11);
- d2.Init(2022, 1, 12);
- d1.Print();
- d2.Print();
- return 0;
- }
复制代码 在上面的一段代码中我们可以看到该类有两个函数,一个是Init,一个是Print,但是在类里面并没有进行d1与d2的区分,那我们是怎样知道我们通过函数设置的是d1变量哪一个设置的是d2变量?在C++中我们就引入了this指针的概念来资助编译器区分:
C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来通报,编译器自动完成。
我们来看下面两段代码示例:
void display()
{
cout<<_year<<endl;
}
void display(Date* this)
{
cout<<this->_year<<endl;
}
这两段代码相比而言,第二串代码把第一串代码中隐藏的this范例显现。
我们来看this指针的特性:
1. this指针的范例:类范例* const,即成员函数中,不能给this指针赋值。
2. 只能在“成员函数”的内部使用
3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地点作为实参通报给this形参。以是对象中不存储this指针。
4. this指针是“成员函数”第一个隐含的指针形参,一样平常环境由编译器通过ecx寄存器自动通报,不需要用户通报
C语言和C++实现stack的区别
1.C语言实现
- typedef int DataType;
- typedef struct Stack
- {
- DataType* array;
- int capacity;
- int size;
- }Stack;
- void StackInit(Stack* ps)
- {
- assert(ps);
- ps->array = (DataType*)malloc(sizeof(DataType) * 3);
- if (NULL == ps->array)
- {
- assert(0);
- return;
- }
- ps->capacity = 3;
- ps->size = 0;
- }
- void StackDestroy(Stack* ps)
- {
- assert(ps);
- if (ps->array)
- {
- free(ps->array);
- ps->array = NULL;
- ps->capacity = 0;
- ps->size = 0;
- }
- }
- void CheckCapacity(Stack* ps)
- {
- if (ps->size == ps->capacity)
- {
- int newcapacity = ps->capacity * 2;
- DataType* temp = (DataType*)realloc(ps->array,
- newcapacity * sizeof(DataType));
- if (temp == NULL)
- {
- perror("realloc申请空间失败!!!");
- return;
- }
- ps->array = temp;
- ps->capacity = newcapacity;
- }
- }
- void StackPush(Stack* ps, DataType data)
- {
- assert(ps);
- CheckCapacity(ps);
- ps->array[ps->size] = data;
- ps->size++;
- }
- int StackEmpty(Stack* ps)
- {
- assert(ps);
- return 0 == ps->size;
- }
- void StackPop(Stack* ps)
- {
- if (StackEmpty(ps))
- return;
- ps->size--;
- }
- DataType StackTop(Stack* ps)
- {
- assert(!StackEmpty(ps));
- return ps->array[ps->size - 1];
- }
- int StackSize(Stack* ps)
- {
- assert(ps);
- return ps->size;
- }
- int main()
- {
- Stack s;
- StackInit(&s);
- StackPush(&s, 1);
- StackPush(&s, 2);
- StackPush(&s, 3);
- StackPush(&s, 4);
- printf("%d\n", StackTop(&s));
- printf("%d\n", StackSize(&s));
- StackPop(&s);
- StackPop(&s);
- printf("%d\n", StackTop(&s));
- printf("%d\n", StackSize(&s));
- StackDestroy(&s);
- return 0;
- }
复制代码 我们可以看到C语言实现有以下特点:
每个函数的第一个参数都是Stack*
函数中必须要对第一个参数检测,因为该参数可能会为NULL
函数中都是通过Stack*参数操作栈的
调用时必须通报Stack结构体变量的地点
结构体中只能界说存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据
的方式是分离开的,而且实现上相当复杂一点,涉及到大量指针操作,稍不注意可能就会出
错。
2.C++实现
- typedef int DataType;
- class Stack
- {
- public:
- void Init()
- {
- _array = (DataType*)malloc(sizeof(DataType) * 3);
- if (NULL == _array)
- {
- perror("malloc申请空间失败!!!");
- return;
- }
- _capacity = 3;
- _size = 0;
- }
- void Push(DataType data)
- {
- CheckCapacity();
- _array[_size] = data;
- _size++;
- }
- void Pop()
- {
- if (Empty())
- return;
- _size--;
- }
- DataType Top() { return _array[_size - 1]; }
- int Empty() { return 0 == _size; }
- int Size() { return _size; }
- void Destroy()
- {
- if (_array)
- {
- free(_array);
- _array = NULL;
- _capacity = 0;
- _size = 0;
- }
- }
- private:
- void CheckCapacity()
- {
- if (_size == _capacity)
- {
- int newcapacity = _capacity * 2;
- DataType* temp = (DataType*)realloc(_array, newcapacity *
- sizeof(DataType));
- if (temp == NULL)
- {
- perror("realloc申请空间失败!!!");
- return;
- }
- _array = temp;
- _capacity = newcapacity;
- }
- }
- private:
- DataType* _array;
- int _capacity;
- int _size;
- };
- int main()
- {
- Stack s;
- s.Init();
- s.Push(1);
- s.Push(2);
- s.Push(3);
- s.Push(4);
- printf("%d\n", s.Top());
- printf("%d\n", s.Size());
- s.Pop();
- s.Pop();
- printf("%d\n", s.Top());
- printf("%d\n", s.Size());
- s.Destroy();
- return 0;
- }
复制代码 C++中通过类可以将数据以及操作数据的方法进行完美结合,通过访问权限可以控制那些方法在
类外可以被调用,即封装,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。
而且每个方法不需要通报Stack*的参数了,编译器编译之后该参数会自动还原,即C++中Stack*
参数是编译器维护的,C语言中需用用户自己维护。
这就是C++中类与对象的第一部分,后续另有两部分,感谢关注哦~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |