【C++】类和对象(一)

1. 类的定义

1.1类定义格式

class 类名{

};

class是定义类的关键字,{}中的为类的主体,类定义结束时最后的分好不能省略。

类体中的内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的类的方法或成员函数。

为了区分成员变量,一般习惯上成员变量会加一个特殊标识,如成员变量前面或后面加_或者m开头,这个不是强制的,只是惯例。

C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的变化是struct中可以定义函数,一般情况下推荐使用class定义类。

class 和struct定义类的区别是class默认访问限定符是私有,struct默认访问限定符是公有。

定义在类里的函数默认是inline。声明和定义分离不是inline。

cpp 复制代码
#include<assert.h>
#include<iostream>
using namespace std;

class Stack {
public:
	// 成员函数
	void Init(int n = 4) {
		array = (int*)malloc(sizeof(int) * n);
		if (array == nullptr) {
			perror("malloc申请空间失败!");
			return;
		}
		capacity = n;
		top = 0;
	}

	void Push(int x) {
		// ...扩容
		array[top++] = x;
	}

	int Top() {
		assert(top > 0);
		return array[top - 1];
	}

	void Destroy() {
		free(array);
		array = nullptr;
		top = capacity = 0;
	}
private:
	// 成员变量
	int* array;
	size_t capacity;
	size_t top;
};// 分号不能省略

int main() {
	Stack st;

	st.Init();

	st.Push(1);
	st.Push(2);

	cout << st.Top() << endl;

	st.Destroy();

	return 0;
}
cpp 复制代码
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;
};
cpp 复制代码
#include<iostream>
using namespace std;

// C++将struct升级成了类
// 1.类里可以定义函数
// 2.struct名称可以代表类型

// C++中兼容C中struct的用法
typedef struct ListNodeC {
	struct ListNodeC* next;
	int val;
}LTNode;

// 不再需要typedef,ListNodeCPP也可以代表类型
struct ListNodeCPP {
	void Init(int x) {
		next = nullptr;
		val = x;
	}

	ListNodeCPP* next;
	int val;
};

int main() {
	return 0;
}

1.2访问限定符

访问限定符有三个:public、private、protected

C++一种实现封装的方式,用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将接口提供给外部的用户使用

public修饰的成员在类外可以直接被访问;protected和private修饰的成员在类外不能直接被访问。

访问限定符的作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止,如果后面没有访问限定符,作用域就到},即类结束。

class定义成员没有被访问限定符修饰时默认为private,struct默认为public。

一般成员变量都会被限制为private/protected,需要给别人使用的成员函数会放为public。

1.3类域

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

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

cpp 复制代码
#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实例化概念

用类类型在物理内存中创建对象的过程,称为类实例化出对象。

类是对象进行一种抽象化描述,是一个模型一样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,用类实例化出对象时才会分配空间。

一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。

cpp 复制代码
#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(2026, 1, 13);
	d1.Print();

	d2.Init(2026, 1, 1);
	d2.Print();

	return 0;
}

2.2对象大小

对象中只存储成员变量,C++规定类实例化的对象也要符合内存对齐的规则

内存对齐规则

  • 第一个成员在与结构体偏移量为0的地址处
  • 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
  • 对齐数 = 编译器默认的对齐数该成员大小 的较小值
  • VS中的默认对齐是8
  • 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍
  • 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
cpp 复制代码
#include<iostream>
using namespace std;

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指针

编译器编译后,类的成员函数默认都会在形参的第一个位置增加一个当前类类型的指针,叫做this指针。比如Date类真实原型为:void Init(Date const this, int year, int month, int day); *

类的成员函数中访问成员变量,本质都是通过this指针访问的,如Init函数中给_year赋值:this->__year = year;

C++规定不能在实参和形参的位置显式的写this指针(编译时编译器会处理),但是可以在函数体内显式的使用this指针。

this指针存在内存的栈区

cpp 复制代码
#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) {
		// 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, 2026, 1, 13);
	d1.Init(2026, 1, 13);
	d1.Print();

	d2.Init(2026, 1, 1);
	d2.Print();

	return 0;
}

4.C语言和C++实现stack对比

C语言

c 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef int STDataType;

typedef struct Stack {
	STDataType* a;
	int top; // 栈顶
	int capacity; // 容量
}ST;

// 初始化
void STInit(ST* pst) {
	assert(pst);

	pst->a = NULL;
	// top指向栈顶元素的下一个位置
	pst->top = 0;
	pst->capacity = 0;
}
// 销毁
void STDestroy(ST* pst) {
	free(pst->a);
	pst->a = NULL;
	pst->capacity = 0;
}

// 入栈
void STPush(ST* pst, STDataType data) {
	assert(pst);
	if (pst->top == pst->capacity) {
		int newCapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, 2 * newCapacity * sizeof(STDataType));
		if (tmp == NULL) {
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newCapacity;
	}
	pst->a[pst->top] = data;
	pst->top++;
}
// 出栈
void STPop(ST* pst) {
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}

// 获取栈顶元素
STDataType STTop(ST* pst) {
	assert(pst);
	assert(pst->top > 0);
	return pst->a[pst->top - 1];
}

// 判空
bool STEmpty(ST* pst) {
	assert(pst);
	return pst->top == 0;
}

// 获取元素个数
int STSize(ST* pst) {
	assert(pst);
	return pst->top;
}

int main() {
	ST s;
	STInit(&s);
	STPush(&s, 1);
	STPush(&s, 2);
	STPush(&s, 3);
	STPush(&s, 4);
	printf("%d\n", STTop(&s));
	printf("%d\n", STSize(&s));

	while (!STEmpty(&s)) {
		printf("%d ", STTop(&s));
		STPop(&s);
	}

	STDestroy(&s);

	return 0;
}

C++

cpp 复制代码
#include<assert.h>
#include<iostream>
using namespace std;

typedef int STDataType;
class Stack {
public:
	//成员函数
	void Init(int n = 4) {
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a) {
			perror("malloc fail");
			return;
		}
		_capacity = n;
		_top = 0;
	}

	void Push(STDataType x) {
		if (_top == _capacity) {
			int newCapacity = _capacity * 2;
			STDataType* tmp = (STDataType*)realloc(_a, newCapacity * sizeof(STDataType));
			if (tmp == NULL) {
				perror("realloc fail");
				return;
			}
			_a = tmp;
			_capacity = newCapacity;
		}
		_a[_top++] = x;
	}

	void Pop() {
		assert(_top > 0);
		--_top;
	}

	bool Empty() {
		return _top == 0;
	}

	int Top() {
		assert(_top > 0);
		return _a[_top-1];
	}

	void Destroy() {
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}

private:
	//成员变量
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};

int main() {
	Stack s;
	s.Init();
	s.Push(1);
	s.Push(2);
	s.Push(3);
	s.Push(4);

	while (!s.Empty()) {
		printf("%d\n", s.Top());
		s.Pop();
	}

	s.Destroy();

	return 0;
}
相关推荐
是娇娇公主~2 小时前
工厂模式详细讲解
数据库·c++
星火开发设计2 小时前
Java面向对象三大特性:封装、继承与多态的深度解析及实战
java·开发语言·microsoft·多态·继承·面向对象·封装
旅途中的宽~3 小时前
【Python】pip install -v e .命令不想自动更新torch版本
开发语言·python·pip
lly2024063 小时前
Vue3 指令详解
开发语言
_OP_CHEN3 小时前
【从零开始的Qt开发指南】(二十三)Qt 界面优化之 QSS 实战指南:从入门到精通,让你的界面颜值飙升!
开发语言·c++·qt·前端开发·界面美化·qss·客户端开发
e***98573 小时前
Java性能优化实战:从原理到案例
java·开发语言·性能优化
HellowAmy3 小时前
我的C++规范 - 跳跃的对象
开发语言·c++·代码规范
lph0093 小时前
QtMqtt 的编译与QT环境加载配置
开发语言·qt
lucky-billy3 小时前
架构设计 - std::forward 条件转换配合万能引用(T&&)来实现完美转发
c++·完美转发·forward·万能引用