c++的类和对象(上)

近听水无声477 2024-08-15 15:35:01 阅读 84

前言

Hello,小伙伴们,今天我们将开启一个新的章节,一起来探寻c++的奥秘。

好,废话不多说我们我们现在开始我们今天的学习!!

1.类的定义

1.1类的定义格式

c++为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。类体中的函数称为类的方法或者是成员函数。为了区分成员变量,一般习惯上成员变量会加一个特殊的标识,如果成员变量前面或者是后面加——或者是m的开头,注意c++中这个是不强制的,只是一些惯例,在一些公司中会有具体的要求。c++中struct也可以定义类,c++兼容c语法中的struct中的用法,同时也对struct中的用法进行了升级,明显的变化是struct中也可以定义函数。一般情况下我们还是更推荐使用class定义类。定义在类中的成员变量函数,默认情况下都是加上inline的。

我们在这里先定义一个类来演示其效果:

<code>class Date

{

public:

void Init(int year, int month, int day)

{

_year = year;

_month = month;

_day = day;

private:

// 为了区分成员变量,⼀般习惯上成员变量

// 会加⼀个特殊标识,如_ 或者 m开头

int _year; // year_ m_year

int _month;

int _day;

};

int main()

{

Date d;

d.Init(2024, 3, 31);

return 0;

}

}

通过的上面的代码,我们可以直观的看到c原因与c++的区别,定义类的操作上使得c++拥有了更加强大功能,使代码也变得更加的简洁。

我们还可以在Date类中定义另一个函数来实现数据的打印,使得功能更加的全面:

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:

// 为了区分成员变量,⼀般习惯上成员变量

// 会加⼀个特殊标识,如_ 或者 m开头

int _year; // year_ m_year

int _month;

int _day;

};

int main()

{

Date d;

d.Init(2024, 3, 31);

d.Print;

return 0;

}

 1.2访问限定符

c++一种实现封装的方式,用类将对象的属性与方法结合在一起,让对象的属性与方法结合在一起,让对象更加的完善,通过访问权限选择性的将其接口提供给外部的用户使用。public修饰的成员变量在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问,protected和private是一样的,到后面的继承章节会着重介绍他们。访问权限的作用域从该访问限定符出现的位置开始直到下一个限定符出现,如果后面没有再出现限定访问符号,作用域到}即类截止。class定义成员的时候没有被限定修饰符修饰时默认设置为private,struct默认设置为public。一般成员变量都会被限制为private或者是protected,需要给别人使用时的成员函数会被修饰为public。

 

1.3类域

类定义了一个新的作用域,类的所有成员都在类的作用域中,在类体外定义成员时,需要使用::作用域操作符指明该函数出自那个类域。

类域影响的是编译的查找规则,下面程序Init如果不指定类域Stack,那么编译器就把Init当成全局函数,那么编译时,如果找不到array等成员的声明/定义在哪里,就会出现报错。指定类域·Stack,就是知道Init成员函数,当前域找不到array等成员,就会到类域中去寻找。

 

<code>#include<iostream>

using namespace std;

class Stack

{

public:

// 成员函数

void Init(int n = 4);

private:

// 成员变量

int* array;

size_t capacity;

size_t top;

};

// 声明和定义分离,需要指定类域

void Stack::Init(int n)

{

array = (int*)malloc(sizeof(int) * n);

if (nullptr == array)

{

perror("malloc申请空间失败");

return;

}

capacity = n;

top = 0;

}

int main()

{

Stack st;

st.Init();

return 0;

}

2.实例化

2.1实例化概念

用类类型在物理的内存中创建对象的过程,称之为类实例化出对象。类时对象进行一种抽象描述,是一个模型一样的东西,限定了类有哪些成员变量,这些成员都是声明没有开辟空间,用类实例化处对象时,才会开辟空间。一个类可以实例化出多少个对象,实例化出的对象占用物理空间,存储类成员变量。打个比方:类实例化出对象就像现实中使用建造图建造房子,类就是设计图,设计图规划了有多少个房间,房间的大小和功能等,但并没有实体的材料建造成型,不能使用,用设计图建造出房子才能住人。同样,类就是像设计图一样的东西,不能存储数据,实例化出的对象则可以分配物理内存来存储数据。

<code>#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()

{

// Date类实例化出对象d1和d2

Date d1;

Date d2;

d1.Init(2024, 3, 31);

d1.Print();

d2.Init(2024, 7, 5);

d2.Print();

return 0;

}

 2.2对象的大小

分析一下类对象中的那些成员呢?类实例化的每一个对象,都拥有独立的空间,,所以对象中肯定包含成员变量,那么成员函数是否包含其中呢?

首先函数被编译后只是一段指令,对象没有办法在存储,这些指令存储在一个特定的区域(代码段),那么对象中非要存储的话,只能是成员函数的指针.在分析一下,对象中是否有存储指针的必要,Date实例化d1,d2两个对象,d1\d2都有自己独立的成员变量_year/_month/_day储存在各自的数据,互不干扰,但是d1和d2的的成员函数的指针确实一样的,存储在类中就有点浪费空间了。

接下来对象的大小计算还是会涉及我们之前在结构体学到的内存对齐,这里我们就不再赘述,还不是很清楚的同学可以再去看看我之前写的这篇文章: http://t.csdnimg.cn/oNC9Y

 这里我们演示一个特殊的按例:

/ 计算⼀下A/B/C实例化的对象是多⼤?

class A

{

public:

void Print()

{

cout << _ch << endl;

}

private:

char _ch;

int _i;

};

class B

{

public:

void Print()

{

//...

}

};

class C

{};

int main()

{

A a;

B b;

C c;

cout << sizeof(a) << endl;

cout << sizeof(b) << endl;

cout << sizeof(c) << endl;

return 0;

}

运行一下,我们来看看会得到怎样的结果:

 上面的程序我们可以看到运行的结果是,B和C的类对象大小是1,为什么没有对象成员但还是会占用存储空间呢?

因为如果一个字节都不给,怎么来表示对象存在过呢?所以这里给了一个字节,存粹就是为了站位标识对象的存在。

3.this指针

Date类中有Init函数和Print函数,函数体中没有关于不同对象的区分,那当d1调用Init和Print函数时,该函数是如何知道该访问的是d1的对象还是d2的对象呢?这里就要看c++给的一个隐含的this指针解决了这里的问题。编译器编译后,类的成员函数默认都会在形参的第一个位置,增加一个当前类类型的指针,叫做this指针。类中的成员函数访问成员变量是,本质都是通过this指针访问的,入Init函数中给_year赋值,this->_year = year.c++规定不能在实参和形参的位置显示this指针(编译时编译器会进行处理),但是在函数体内可以将this指针显示使用。 

<code>#include<iostream>

using namespace std;

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;

}

private:

// 这⾥只是声明,没有开空间

int _year;

int _month;

int _day;

};

int main()

{

// Date类实例化出对象d1和d2

Date 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;

}

好,今天的学习就到这里,咱们下期再见,拜拜!!! 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。