当前位置 : 主页 > 编程语言 > c语言 >

类和对象初学(上)

来源:互联网 收集:自由互联 发布时间:2023-08-28
什么是类和对象 首先C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完 成。为了表示一个对象c++引入了类的概念,c++的类和c的结构体不同点有

什么是类和对象

首先C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完

成。为了表示一个对象c++引入了类的概念,c++的类和c的结构体不同点有很多,其中一个点就是c++的类中可以写函数,而c的结构体显然是不能写函数的。在c++中类的定义是下面这样

class className
{
// 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分

号不能省略。 类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者 成员函数。

当然在c++中你也可以使用struct去定义一个类,区别也就是访问限定符不一样(下面说了)。

类的定义有两种方法1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。

2.将声明和定义分开,就如同c写一个函数一样,在.h文件中放类的头文件定义,cpp文件中实现类中的函数。

需要注意使用第二种方法的时候要完成类中的函数需要使用::符号。

类的访问限定符以及封装

如果使用c完成一个栈,那么在主函数中的人可以直接访问数组来获取栈中的值,但是若你写的栈,其中的top指向的是栈顶元素的下一个,但是还是会有一些人在主函数中直接使用stack[top]去获取栈顶的元素,然后就会发现,得到了一个随机值。必须去看底层实现代码才会发现要获取栈顶元素需要使用stack[top-1]去获取栈顶元素。为了解决这种情况c++中就引入了访问限定符,访问限定符有三个分别是private,protect,public。

下面是访问限定符的说明

  1. public修饰的成员在类外可以直接被访问
  2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. 如果后面没有访问限定符,作用域就到 } 即类结束。
  5. class的默认访问权限为private,struct为public(因为struct要兼容C)

首先要知道面对对象的三大特性就是:封装、继承、多态。

那么封装是什么呢?

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来

和对象进行交互。

在c++中也正是有了访问限定符的存在也让封装成为了现实。较为正式的说法也就是:在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。

类的作用域

在一开始我说果在.cpp文件中要实现,.h文件中的函数时要使用::,这个符号的作用也就是声明这个函数属于这个作用域。

例如下面这样

放在.h文件中

class Stack
{
public:
	void Init();//初始化栈,在stack这个作用域里声明一个Init函数
	//用于初始化栈
private:
	int* a;//定义了个成员变量
	int capacity;//定义容量
	int top;
};//在这里声明一个类为栈

放在.cpp文件中

void Stack::Init()//通过::声明Init这个函数属于stack作用域
{
	a = NULL;
	capacity = 0;
	top = 0;
}//作用为将栈初始化

类的实例化

如果将在.h文件中的类比作一个建筑图纸的化,那么类的示例化也就是通过这个建筑图纸建设一所又一所的房子,这些房子每一所都是对立的但是每一个都是由建筑图纸而来的。这一个过程也就是类的实例化,即通过类类型来创建对象。一个类可以实例化多个对象,实例化出的对象,占用实际的物理空间,储存成员变量。

类对象模型的储存方式

下面学习如何计算一个类的大小,首先要知道由类实例化出的对象,这个对象储存在栈中,但是类的函数却不是储存在这个对象中的。如果类中的函数也会储存在对象中,那么每实例化一个对象,就会多浪费一些空间用来储存函数。所以在使用一个类实例化一个对象之后,这个对象中的函数会储存在一个公共区域。如果再使用一个类实例化一个对象之后就只用分配空间去储存成员变量。而函数则直接使用公共区域的函数即可。下面是图解

类和对象初学(上)_实例化



那现在这里又出现了一个问题,那就是编译器是怎么判定这个函数要调用给哪一个对象呢?这就要使用this指针了。

下面我写一个日期类

class Data
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void print()
	{
		cout << _year << endl;
		cout << _month << endl;
		cout << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

但其实在编译器底层Init函数是下面这样,只是这个this指针是由编译器自己传入自己使用的

void Init(Data* this, int year, int moonth, int day)
	{
		this->_day = day;
		this->_month = month;
		this->_year = year;
	}

可以使用返回编来观察

类和对象初学(上)_成员函数_02

可以看到第四行有一个lea,这个指令的作用为取地址,这里也就是取出d1的地址放到ecx寄存器中,然后再去调用Init函数。也正是因为this指针编译器才能知道这个是哪一个对象调用的函数。

下面还有this指针的特性

  1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
  2. 只能在“成员函数”的内部使用
  3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储this指针。
  4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递

下面有一些题(基于类中的函数不储存在对象中)

// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
	void Print()
	{
		cout << "Print()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

首先这道题目肯定不能选编译错误,因为这道题目很显然是因为空指针而可能会导致出错的,但是编译器并不会显示空指针错误,所以A排除。再分析B运行崩溃,那么什么时候会造成运行崩溃呢?很显然那就是当对nullptr进行解引用就会造成运行崩溃,但是这道题目是在使用Print函数并且Print函数中没有去对p进行解引用,而Print函数又储存在公共区域所以这道题目选c。

题目二:

class A
{
public:
    void PrintA()
    {
        cout << _a << endl;
    }
private:
    int _a;
};
int main()
{
    A* p = nullptr;
    p->PrintA();
    return 0;
}

那么这道题很显然要选择B,运行崩溃了,因为在 PrintA函数中很显然对P进行了解引用去访问它的成员变量了,而p为空指针所以运行崩溃了。

计算类对象的大小

要知道类对象的大小是如何计算的首先就要知道类的储存方式,而这一点在上面已经说过了。

下面给出几个类判断它们的大小。

// 类中既有成员变量,又有成员函数
class A1 {
public:
    void f1() {}
private:
    int _a;
};
// 类中仅有成员函数
class A2 {
public:
	void f2() {}
};
// 类中什么都没有---空类
class A3
{};
int main()
{
    cout <<"A1:" << sizeof(A1) << endl;
    cout <<"A2:" << sizeof(A2) << endl;
    cout <<"A3:" << sizeof(A3) << endl;
    return 0;
}

类和对象初学(上)_实例化_03

可以看到A1的大小为4字节,那是因为在A1类中含有一个_a成员变量,那么为什么A2和A3中没有成员变量甚至于,A3为一个空类还会有一个字节的大小呢?

这一个字节大小的空间就是为了占位,假设没有这一个字节大小的空间,但是有人在主函数中使用A2或是A3类实例化了一个对象,如果不占位,实例化对象放在哪里呢?所以A2和A3类即使没有成员函数也会使用1个字节的空间去进行占位。

除此之外计算类的方法和计算c中结构体的方式也是一样的,都要遵循内存对齐。

那么为何要内存对齐呢?

假设这里有一个类A其中的成员变量为个int 和一个char。

然后假设cpu访问数据都是4个字节4个字节的访问请看下图:

类和对象初学(上)_成员变量_04

这也是为何进行内存对齐。

正在初学c++的萌新,如果您发现了任何错误,欢迎您指出,我一定修改。




上一篇:超全的pandas数据分析常用函数总结:下篇
下一篇:没有了
网友评论