C++ -2- 类和对象(上)


文章目录

  • 1.面向过程与面向对象
  • 2.类的引入
  • 3.类的定义
    • 两种定义方式
  • 4.类的访问限定符
  • 5.类的作用域
  • 6.类的示例化
  • 7.类的对象大小计算
  • 8.类成员函数的this指针
    • C语言和C++的对比(this指针)
    • 空指针的问题
  • C语言和C++实现Stack对比

1.面向过程与面向对象

  • C:面向过程,关注的是过程分析出求解问题的步骤,通过函数调用逐步解决问题。
  • C++:面向对象,关注的是对象将一件事情拆分成不同的对象,靠对象之间的交互完成。

2.类的引入

  • C:struct 结构体,定义成员变量
  • C++:struct 定义成员变量和函数

C++ 把 结构体“升级成了” structclass

//C:
struct ListNode
{
	int* _a;
	int _size;
	int _capacity;
};


//CPP:
struct ListNode2
{
	void Init(size_t capacity)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (!_a)
		{
			perror("malloc fail");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}

	//……

	int* _a;
	int _size;
	int _capacity;
};

3.类的定义

class classname
{    
    类体:由“类的成员”组成
    ……
}
  • 类的成员:
    • 变量 → 类的属性/成员变量
    • 函数 → 类的方法/成员函数

两种定义方式

  1. 声明和定义全在类体中 → 日常练习(注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。)
//CPP:
struct ListNode2
{
	void Init(size_t capacity)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (!_a)
		{
			perror("malloc fail");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}

	//……

	int* _a;
	int _size;
	int _capacity;
};
  1. 声明放在.h头文件,定义放在.cpp文件 → 正式工作
//.h
class fantasy
{
	void Init(int capacity = 4);

	//……

	int* _a;
	int _size;
	int _capacity;
};

//.cpp
#include "test.h"
void fantasy::Init(int capacity)//意思是“Init”这个函数是“fantasy”这个类里的成员函数
{
	_a = (int*)malloc(sizeof(int) * capacity);
	if (!_a)
	{
		perror("malloc fail");
		return;
	}
	_capacity = capacity;
	_size = 0;
}
  • 成员变量命名规则的建议:
class DateRB
{
public:
	void Init(int year = 0, int month = 0, int day = 0)
	{
		year = year;
		month = month;
		day = day;
		//这样就很奇怪,到底是谁赋值给谁
	}
private:
	int year;
	int month;
	int day;
};

所以,我们建议区别命名:

class DateRB
{
public:
	void Init(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

4.类的访问限定符

  1. 公有(public)→ struct 默认
  2. 保护(protected)
  3. 私有(private)→ class 默认

一般:

class MyClass
{
public:
	成员函数;

private:
	成员变量;
};
  • 封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

5.类的作用域

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

class fantasy//新的作用域
{
	void Init(int capacity = 4);
	//……
	int* _a;
	int _size;
	int _capacity;
};

void fantasy::Init(int capacity)//指明成员属于哪个类域。
{
	_a = (int*)malloc(sizeof(int) * capacity);
	if (!_a)
	{
		perror("malloc fail");
		return;
	}
	_capacity = capacity;
	_size = 0;
}

6.类的示例化

类 → 相当于设计图
类的实例化 → 相当于根据设计图建房

class DateRB
{
public:
	void Init(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	DateRB d1;//类的实例化 → 对象
	d1.Init();

	return 0;
}

7.类的对象大小计算

class DateRB
{
public:
	void Init(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;//4→ 0,1,2,3
	int _month;//4→ 4,5,6,7
	int _day;//4→ 8,9,10,11
	//内存:12byte
};

int main()
{
	DateRB d1;//类的实例化
	d1.Init();

	cout << sizeof(d1) << endl;//output:12

	return 0;
}

如上这个例子,sizeof(d1) = 12(byte)

  • 为什么成员变量在对象(d1)里,成员函数不在对象里?
    • 每个对象的成员变量不一样,需要单独存储
    • 调用的成员函数都是一样的,在公共区域 (代码段)

特别的:

// 类中仅有成员函数
class A2 {
public:
   void f2() {}
};
// 类中什么都没有---空类
class A3
{};

sizeof(A2) : ?
sizeof(A3) : ?

cout << sizeof(A2) << endl;//output:1
cout << sizeof(A3) << endl;//output:1

1 字节 不存储有效数据,只是占位,用来标识 对象被实例化出来。


8.类成员函数的this指针

C++ -2- 类和对象(上)_第1张图片

C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。这个过程编译器自动完成。

void Init(int year = 0, int month = 0, int day = 0)
{
	_year = year;
	_month = month;
	_day = day;
}

//实际上:
void Init(DateRB* this, int year = 0, int month = 0, int day = 0)
{
	this->_year = year;
	this->_month = month;
	this->_day = day;
}

C语言和C++的对比(this指针)

C++ -2- 类和对象(上)_第2张图片
C++ -2- 类和对象(上)_第3张图片
对象 d1 调用函数时,this = &d1 这个地址会传给函数

  • this 指针 → 隐含的形参,储存在

空指针的问题

定义:DateRB* ptr = nullptr;

class DateRB
{
public:
	void Init(int year = 0, int month = 0, int day = 0)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void func()
	{

	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	DateRB* ptr = nullptr;
	ptr->func();//正常运行
	ptr->Init(2023, 2, 27);//运行崩溃
	(*ptr).func();//正常运行
	return 0;
}
  1. ptr->func();//正常运行
    C++ -2- 类和对象(上)_第4张图片
  • 为什么会正常运行?
    • 此过程中,调用 func 函数,只是把 ptr 的内容(nullptr)传给了 this 指针,而函数运行过程中 没有对 this 指针解引用操作
  1. ptr->Init(2023, 2, 27);//运行崩溃
void Init(int year = 0, int month = 0, int day = 0)
{
	this->_year = year;
	this->_month = month;
	this->_day = day;
}

显然,Init 函数会对 this 指针 解引用操作对空指针解引用造成运行崩溃

  1. (*ptr).func();//正常运行
    ptr->func();一样,没有对this 指针解引用
  • sum.看到 '*' 或者 '->' 不一定进行了解引用操作

注意:不能这样调用函数

func();
DateRB::func();

C语言和C++实现Stack对比

  • CPP:

    1. 数据和方法都封装到类里面
    2. 控制访问方式(共有、私有)
  • C:

    1. 数据和方法分离
    2. 数据的访问控制是自由的,不受限制的
      • 譬如:取栈顶的元素,可以不通过调用函数的方式而直接访问

      • cpp Stack st; int top = st.a[st.top] 但是这需要了解栈的底层结构,并且容易出错


END

你可能感兴趣的:(C++,c++,开发语言)