赞
踩
class 为定义类的关键字,Stack 为类的名字, { } 中为类的主体,注意类定义结束时后面分号不能省略。类体中内容称为类的成员,类中的变量称为类的属性或成员变量,类中的函数称为类的方法或者成员函数。
为了区分成员变量,一般习惯上成员变量会加一个特殊标识,如成员变量前面或者后面加 “ _ ” 或者 “ m ” 开头,注意C++中这个并不是强制的,只是一些惯例。
C++中 struct 也可以定义类,C++兼容C中 struct 的用法,同时 struct 升级成了类,明显的变化是 struct 中可以定义函数,一般情况下我们还是推荐用 class 定义类。
定义在类里面的成员函数默认加上 inline 。
#include <iostream> class Stack { public: // 成员函数 void init(int n = 4) { _arr = nullptr; _capacity = _size = 0; checkCapacity(); } int top() { return _arr[_size - 1]; } void pop() { --_size; } void push(int x) { checkCapacity(); _arr[_size++] = x; } void destroy() { free(_arr); _arr = nullptr; _capacity = _size = 0; } void checkCapacity() { if (_capacity <= _size) { int newCapacity = _capacity == 0 ? 4 : _capacity * 2; int* temp = (int*)realloc(_arr, sizeof(int) * newCapacity); if (temp == nullptr) { perror("realloc faild"); return; } _arr = temp; _capacity = newCapacity; } } private: // 成员变量 int* _arr; int _capacity; int _size; }; int main() { Stack st; st.init(); st.push(1); st.push(2); st.push(3); st.destroy(); return 0; }
C++一种实现封装的方式,用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
public 修饰的成员在类外可以直接被访问,protected 和 private 修饰的成员在类外不能直接被访问,protected 和 private 在这方面的作用是一样的。(在继承中才可体现他们的区别)
访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止,如果后面没有访问限定符,作用域就到 “ } ” 即类结束。
class 定义成员没有被访问限定符修饰时默认为 private,struct 默认为 public。
一般成员变量都会被限制为 private / protected,需要给别人使用的成员函数会修饰为 public。
class Student
{
// 访问限定符
public:
protected:
private:
};
class Stack { public: void init(int x); private: int* _arr; int _capacity; int _size; }; // 声明和定义分离 void Stack::init(int x) { //... }
类实例化出的每个对象,都有独立的数据空间,所以对象中肯定包含成员变量。但成员函数不一样,首先函数被编译后是一段指令,对象中没办法存储,这些指令存储在一个单独的区域(代码段),那么对象中非要存储的话,只能是成员函数的指针。并且对象中也没有存储其指针的必要,Date 实例化 d1 和 d2 两个对象,d1 和 d2 都有各自独立的成员变量 _year / _month / _day 存储各自的数据,但是 d1 和 d2 的成员函数 init / print 指针却是一样的,存储在对象中就浪费了。如果用 Date 实例化 100 个对象,那么成员函数指针就重复存储 100 次,太浪费了。
#include <iostream> using namespace std; class Date { public: void init(int year = 2000, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } void print() { cout << _year << "/" << _month << "/" << _day << endl;; } private: // 只是声明,没有开空间 int _year; int _month; int _day; }; int main() { Date d1; Date d2; d1.init(2024, 8, 2); d1.print(); d2.init(2014, 8, 2); d2.print(); return 0; }
上面我们分析了对象中只存储成员变量,C++规定类实例化的对象也要符合内存对齐的规则。
内存对齐规则:
博客 (学习总结6)C语言结构体的内存对齐和位段实现 对内存对齐有详细的解释,这里不赘述。
没有成员变量的类为表示对象存在,给定 1 字节,纯粹为了占位标识对象存在。
#include <iostream> using namespace std; class Student { ; }; int main() { Student a; // 大小都为 1 字节 cout << sizeof(a) << endl; cout << sizeof(Student) << endl; return 0; }
Date 类中有 init 与 print 两个成员函数,函数体中没有关于不同对象的区分,那当 d1 调用 init 和 print 函数时,为分辨应该访问的是 d1 对象还是 d2 对象,C++给了一个隐含的 this指针 解决这个问题。
编译器编译后,类的成员函数默认都会在形参第一个位置,增加一个当前类类型的指针,叫做 this指针。比如 Date 类的 init 的真实原型为,void init(Date* const this, int year, int month, int day)
类的成员函数中访问成员变量,本质都是通过 this指针 访问的,如 init 函数中给 _year 赋值,可改为 this->_year = year。
C++规定不能在实参和形参的位置显示的写 this 指针(编译时编译器会处理),但是可以在函数体内显示使用 this 指针。
this指针 存放在寄存器中。
class Date { public: //void init(Date* const this, int year = 2000, ...) void init(int year = 2000, int month = 1, int day = 1) { // 不能修改 //this = nullptr; //_year = year; //_month = month; //_day = day; this->_year = year; this->_month = month; this->_day = day; } void print() { cout << _year << "/" << _month << "/" << _day << endl;; } private: int _year; int _month; int _day; };
面向对象三大特性:封装、继承、多态,下面的对比可以初步体现封装。
通过下面两份代码对比,我们发现C++实现 Stack 形态上还是发生了挺多的变化,底层和逻辑上没啥变化。
C语言版本 Stack,详细可参考博客 栈的讲解与实现
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <stdbool.h> #define INIT_CAPACITY 4 #define EXPANSION_MULTIPLE 2 typedef int STDataType; typedef struct Stack { STDataType* arr; int size; int capacity; } Stack, * pStack; bool StackEmpty(pStack pst) { assert(pst); return pst->size == 0; } void StackInit(pStack pst) { assert(pst); pst->arr = NULL; pst->size = 0; pst->capacity = 0; } void StackDestroy(pStack pst) { assert(pst); free(pst->arr); pst->size = 0; pst->capacity = 0; } void StackPush(pStack pst, STDataType x) { assert(pst); if (pst->size == pst->capacity) { int newCapacity = pst->capacity == 0 ? INIT_CAPACITY : pst->capacity * EXPANSION_MULTIPLE; STDataType* temp = (STDataType*)realloc(pst->arr, newCapacity * sizeof(STDataType)); if (temp == NULL) { perror("realloc failed"); return; } pst->arr = temp; pst->capacity = newCapacity; } pst->arr[pst->size++] = x; } void StackPop(pStack pst) { assert(pst); assert(!StackEmpty(pst)); --pst->size; } STDataType StackTop(pStack pst) { assert(pst); assert(!StackEmpty(pst)); return pst->arr[pst->size - 1]; } int StackSize(pStack pst) { assert(pst); return pst->size; }
C++版本 Stack:
#include <iostream> #include <assert.h> using namespace std; const int INIT_CAPACITY = 4; const int EXPANSION_MULTIPLE = 2; typedef int STDataType; class Stack { public: bool empty(); void init(); void destroy(); void push(STDataType x); void pop(); STDataType top(); int size(); private: STDataType* _arr; int _size; int _capacity; }; bool Stack::empty() { return _size == 0; } void Stack::init() { _arr = nullptr; _size = _capacity = 0; } void Stack::destroy() { free(_arr); _arr = nullptr; _size = _capacity = 0; } void Stack::push(STDataType x) { if (_size == _capacity) { int newCapacity = _capacity == 0 ? INIT_CAPACITY : _capacity * EXPANSION_MULTIPLE; STDataType* temp = (STDataType*)realloc(_arr, newCapacity * sizeof(STDataType)); if (temp == nullptr) { perror("realloc failed"); return; } _arr = temp; _capacity = newCapacity; } _arr[_size++] = x; } void Stack::pop() { assert(!empty()); --_size; } STDataType Stack::top() { assert(!empty()); return _arr[_size - 1]; } int Stack::size() { return _size; } int main() { Stack a; a.init(); a.push(1); a.push(2); a.push(3); a.push(4); while (!a.empty()) { cout << a.top() << endl; a.pop(); } a.destroy(); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。