类与对象(上)
1.引入
C语言是面向过程的,但C++是面向对象的,这一点很好地体现在了类上面,什么是类?
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如: 之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现, 会发现struct中也可以定义函数,这时struct就上升到了类:
typedef int DataType;
struct Stack
{void Init(size_t capacity){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (nullptr == _array){perror("malloc申请空间失败");return;}_capacity = capacity;_size = 0;}void Push(const DataType& data){// 扩容_array[_size] = data;++_size;}DataType Top(){return _array[_size - 1];}void Destroy(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}DataType* _array;size_t _capacity;size_t _size;
};
int main()
{Stack s;s.Init(10);s.Push(1);s.Push(2);s.Push(3);cout << s.Top() << endl;
}
在这个用c++中的类模拟实现的栈中,在类中定义了函数,并且在类中对变量定义的顺序也没有特别规定,c++默认都在一个类中,是一个整体,并且可以在其中定义函数。
2.类的定义
补充:C++中struct和class的区别是什么?
C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类默认访问权限是private。
class Date
{
public:void Init(int year){_year = year;}
private:int _year;//这里加斜杠就是防止形参与这里的变量混淆,以示区分
};
1.用类类型创建对象的过程,称为类的实例化,类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
typedef int DataType;
class Stack
{
public:void Init(size_t capacity){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (nullptr == _array){perror("malloc申请空间失败");return;}_capacity = capacity;_size = 0;}void Push(const DataType& data){// 扩容_array[_size] = data;++_size;}DataType* _array;size_t _capacity;size_t _size;
};
int main()
{Stack s;s._capacity = 1;return 0;
}
这里必须先要创建一个栈出来,不能直接对类中的元素赋值,可以理解为类只是一张图纸,什么都没有,只有根据这张图纸盖出房子来,才可以住人。
3.this指针
先来看一段代码的结果:
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;d1.Init(2022, 1, 11);d2.Init(2022, 1, 12);d1.Print();d2.Print();return 0;
}
明显这其中的Init函数以及Print函数都是放在公共代码段的,那么为什么会打印出不一样的结果呢?为什么不会发生冲突呢?
这是因为this的作用,C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
// 1.下面程序编译运行结果是?
class A
{
public:void Print(){cout << "Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}
这里打印print()并没有对this解引用,因此即使this指针为空,运行也是正确的!
// 1.下面程序编译运行结果是?
class A
{
public:void PrintA(){cout << _a << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->PrintA();return 0;
}
这时候就不一样了,将this的空指针传值过去后,对this试图解引用访问_a,这当然是错误的,这时就会报错。
4.默认成员函数
4.1构造函数
如何对一个类进行初始化呢?我们可以在类中写一个init函数,然后完成类的实例化之后,调用类中的初始化函数,在C++11中存在另一种初始化方式;
概念:构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次。
特性:构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。
typedef int DataType;
class Stack
{
public:Stack(DataType* a, int n){cout << "Stack(DataType* a, int n)" << endl;_array = (DataType*)malloc(sizeof(DataType) * n);if (NULL == _array){perror("malloc申请空间失败!!!");return;}memcpy(_array, a, sizeof(DataType) * n);_capacity = n;_size = n;}Stack(int capacity = 4){cout << "Stack(int capacity = 4)" << endl;_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){CheckCapacity();_array[_size] = data;_size++;}void Pop(){if (Empty())return;_size--;}DataType Top() { return _array[_size - 1]; }int Empty() { return 0 == _size; }int Size() { return _size; }private:void CheckCapacity(){if (_size == _capacity){int newcapacity = _capacity * 2;DataType* temp = (DataType*)realloc(_array, newcapacity * sizeof(DataType));if (temp == NULL){perror("realloc申请空间失败!!!");return;}_array = temp;_capacity = newcapacity;}}
private:DataType* _array;int _capacity;int _size;
};
上面这段代码演示了用类来作为构造函数名并且构造函数构成重载。
如果我们在main函数中这样去使用构造函数呢?
int main()
{Stack s(1);Stack ss();return 0;
}
那么第一种就是正确的,而第二种是错误的,原因是:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明,编译器会报警告:
1>\test.cpp(370,8): warning C4930: “Stack ss(void)”: 未调用原型函数(是否是有意用变量定义的?)
class Date
{
private:// 基本类型(内置类型)int _year;int _month;int _day;// 自定义类型Time _t;
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
};
int main()
{Date d;d.Print();return 0;
}
结果却是随机值,这是为什么?我们不写构造函数,那么编译器会自动调用默认构造函数,通常对内置类型不做处理,这在不同的编译器上有着不同的效果,有的可能会主动赋值为0,这里演示的是在vs2022上的处理结果,而自定义类型会去调用它的默认构造。
难道只能让编译器产生随机值吗?C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。
例如: