C++:类与对象——第一期

一、类与结构体前世孽缘

        在C语言中,我们要描述一本书的是,需要去指出书名,作者,出版日期,价格;而仅仅从数据的单个属性去介绍这个书,是完全不够的。所以我们引入了结构体;结构体中允许,包含多个数据类型,如字符、整型、浮点型、数组、指针等等。而这些数据类型被称为成员变量,又称作成员属性。——【C语言】结构体与位段-CSDN博客

  • 好消息,在C++中,结构体进一步升级成为了类
  • C++兼容C语言中结构体的用法
  • 类不但可以包含多个成员变量,而且可以包含成员函数(函数的声明与定义)
  • 成员变量又叫做类的属性,成员函数又叫做类的方法
  • 在类中定义与声明合并的函数方法,默认是内联函数
  • 创建对象的时候,只需要类名即可,不用再加上类的关键字
  • 声明类的关键字又引入一个:class(类、类型)
  • class与struct声明类的使用,除了默认访问限定符不一样,其余的一致。

二、类的基础概念

1.类的定义格式
  • 类的定义格式:class myclassName {   //类的方法、类的属性  };
2.对象的内存占用

        用类去创建一个对象,系统会在内存中为其开辟空间;那么问题又来了:对象所占用的内存大小该如何计算呢?——对象中主要存储的是成员变量的数据,而成员变量的内存存储遵循内存对齐的原则。

  • 问:对象是如何去访问类的方法的?
  • 答:函数在经过编译后会生成一段代码指令,代码指令是无法存储在对象中的,而是存储在代码段中的,而函数的地址就是第一条指令的地址,对象通过该地址去调用函数。
  • 问:类的属性存储在对象中毋庸置疑,但问题是:函数地址是存储在对象中的吗
  • 答:错,函数地址也并非存在对象中,也是在代码段中;因为,不同的对象调用函数的时候,都是通过相同的函数地址去调用的,所以将函数地址存储在内存中,会造成内存的浪费。
  • 问:若类中没有成员变量,只有成员函数时,对象在占用内存的大小会不会是0呢?
  • 答:不会,没有成员变量的对象,占用内存的大小为1;若对象的大小是0,那怎么在内存中标识对象是否存在呢?你说呢?

3.类的访问权限

        讲起访问限定符了,就不得不提一下C++的其中一个特性:封装,在C++中,不太喜欢(也不愿)能在外部,随意访问与修改数据的大小,又或者个人不想让别人使用某个类的方法或访问某个成员数据,与是就引入的访问限定符:public、project、private。

  • 通过访问限定符对成员变量和成员函数进行修饰,可以对其访问权限进行修改
  • public修饰的成员变量与成员函数,可通过对象进行访问;
  • private与project修饰的成员变量与函数,不可通过对象进行访问;
  • 访问限定符的作用域在两个访问限定符之间,又或者是类结束——‘}’
  • class声明的类,默认是private修饰
  • struct声明的类,默认是public修饰
4.类域
  • 类形成了一个新域,在类外,定义成员变量与函数的时候,需要通过域操作符(::)去访问。
  • 编译器默认查找路径是,当前域,全局域,若找不到,则判定链接错误。
  • 定义成员变量,这里定义的成员变量是指的是,定义一个静态的成员变量,而不是非静态的。
  • 定义成员函数,是用于函数声明与定义分离的时候,在类外,定义函数的使用,需要告诉编译器我定义的函数是在某个域中的。
class ZMH
{
public:
	static void Print();
private:
	static int _test;
	int _a;
	int _b;
};

// 成员变量的定义
int ZMH::_test = 1314;

// 成员函数的定义
void ZMH::Print()
{
	cout << ZMH::_test;
}

int main()
{
	ZMH::Print();
	return 0;
}

三、抽象与实例化

       在现实生活中,建造别墅需要用到设计图纸:里面包含了别墅的大部分信息,以及别墅的整体轮廓。而建筑工人则根据这种设计图纸,可以建造出一套别墅,且可以建造多套别墅。也可以存在多套别墅,允许这多套别墅相同,或有些细节不同。

        而类就好似一张设计图片,创建对象类似于建造一套别墅。类是对象的抽象,对象是类的实例化。所以,声明类的时候,是没有开辟成员变量的空间的,而用类创建对象的时候,才是真正的开辟空间。

四、相关代码+注释解析

        下面是用类封装了一个顺序表,主要是想让大家,可以结合着代码,和代码注释,对类的基本概念,有更深入的理解。

#include<iostream>
#include<assert.h>
using namespace std;

typedef int T; // 本来是个类的模板,因为还未涉及;
class SeqList
{
public:
	SeqList(int n = 4)
		:_val(new T[n])
		, _size(0)
		, _capacity(n) {}
	~SeqList()
	{
		delete[] _val;
		_size = _capacity = 0;
	}
	void Chack() //函数直接在类中定义与声明一起,默认内联函数。
	{
		if (_size >= _capacity)
		{
			T* tmp = new T[2 * _capacity];
			memcpy(tmp, _val, sizeof(T) * _capacity);
			delete[] _val;
			_val = tmp;
			_capacity *= 2;
		}
	}
	void Print() //函数直接在类中定义与声明一起,默认内联函数。
	{
		for (int i = 0; i < _size; ++i)
			cout << _val[i] << " ";
		cout << endl;
	}
	void Insert(T x, int k); // 函数声明
	void Remove(int k);
	int  Find(T x);
	void Revise(T x, int k);
private: //私有,类外不能直接访问
	T* _val;
	int _size;
	int _capacity;
};

void SeqList::Insert(T x, int k) //声明与定义分类,如何去定义类域中的函数
{
	Chack();
	assert(k >= 1 && k <= _size + 1);

	int end = _size - 1;
	while (end >= k - 1)
	{
		_val[end + 1] = _val[end];
		end--;
	}

	_val[k - 1] = x;
	++_size;
}


void SeqList::Remove(int k)
{
	assert(k >= 1 && k <= _size);

	int start = k - 1;
	while (start + 1 < _size)
	{
		_val[start] = _val[start + 1];
		start++;
	}

	--_size;
}

int SeqList::Find(T x)
{
	for (int i = 0; i < _size; ++i)
	{
		if (_val[i] == x)
		{
			return i;
		}
	}
	return -1;
}

void SeqList::Revise(T x, int k)
{
	assert(k >= 1 && k <= _size);
	_val[k - 1] = x;
}

int main()
{
	SeqList s1(4); // 通过声明的类创建对象

	s1.Insert(1, 1); // 通过对象,函数方法的调用
	s1.Insert(2, 1);
	s1.Insert(3, 1);
	s1.Insert(4, 1);
	s1.Insert(5, 1);
	s1.Print();
	cout << s1.Find(1) << endl;
	s1.Remove(3);
	s1.Print();
	s1.Revise(1314, 3);
	s1.Print();

	return 0;
}

五、this指针 

  • 类的方法是存储在代码段中的,不同的类,调用相同的代码指令,那么问题就来了:
  • 不同的对象是调用函数的时候,是如何访问不同对象中的数据呢?编译器又是如何知道,我要访问哪个对象的数据呢?
  • 这里,就不得不提一下隐式指针的功能了——它是由this关键字起作用,其指向的是,通过谁调用函数的,谁。
  • this指针不可在形参和实参中显示存在,编译器会自己做处理。
  • 但是在函数内部,this指针可以显示存在。
int main()
{
	SeqList s1(4); 
    //s1.Insert(&s1,1,1); ->编译器在调用的时候,在第一个形参前面的位置,加上对象的地址,
	s1.Insert(1, 1); 
	s1.Insert(1, 1);
	s1.Insert(1, 1);
	s1.Insert(1, 1);

    SeqList s2; 
	s2.Insert(2, 1); 
	s2.Insert(2, 1);
	s2.Insert(2, 1);
	s2.Insert(2, 1);

   
    //s1.Insert(&s1,1,1); ->编译器会自己做处理,
    s1.Print(); // 结果是1 1 1 1

    //s1.Insert(&s2,1,1);->在形参和实参位置,不可显示调用,
	s2.Print(); // 结果是2 2 2 2

	return 0;
}
  • 27
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值