前言:
在C++学习以及面向对象编程中,类和对象的重要性不言而喻。它们不仅是理解计算机科学中抽象概念的关键工具,同时也是构建模块化、可维护和可扩展软件系统的基础。
当我们定义一个类时,实际上是在创建一个抽象的模板,用于描述某种事物或概念的属性和行为。这个模板是程序员对现实世界中事物的抽象,并在计算机中形成了一个可操作的实体。例如,一个学生类可以被定义为具有姓名、年龄和成绩等属性,以及查询成绩、修改个人信息等行为的方法。
而对象则是这个抽象模板的具体实例,是在程序中根据类定义创建的实体。每个对象都拥有类所描述的属性和行为,并且可以根据需要执行特定的操作。例如,我们可以创建多个学生对象,每个对象代表不同的学生,拥有自己的姓名、年龄和成绩等属性,并且可以执行查询成绩、修改信息等操作。
通过类和对象,我们可以以一种更加直观和自然的方式来描述和处理问题。通过抽象概念,我们可以将复杂的现实世界映射到计算机中,从而更容易地理解和管理系统。同时,类和对象的重用性和扩展性使得代码更易于维护和扩展,促进了软件开发的高效性和可维护性。
基本介绍
class Date
{
public://公有,声明函数
void Print();
private://私有,保存对象的数据
int _year;
int _month;
int _day;
};
一个类通常包含两部分 private 和 public 他们包含的数据也就向他们的单词意思一样,私有 和 公有 这就是类非常直观的数据封装的做法,我们可以通过类来调用我们实现的函数接口,我们无法直接取接触对象数据,而是通过函数接口来获得处理后的数据,这是C++中与C语言中结构体不同的地方:C语言中结构体内只能保存我们的数据,而无法对其进行限制,也就是说,C语言中我们可以直接访问对象数据甚至修改;C++中也可以使用struct定义对象,他与class的区别是,struct默认数据是公有类型,而class是私有类型
this指针
在C++中,`this` 指针是一个特殊的指针,它指向当前对象的地址。它是在每个非静态成员函数中隐式地传递给该函数的,用于访问调用该函数的对象的成员变量和方法。
`this` 指针的主要作用是在类的成员函数中引用当前对象。当对象调用成员函数时,编译器会自动将该对象的地址传递给 `this` 指针。通过 `this` 指针,成员函数可以访问该对象的成员变量和方法,即使成员变量和方法名与参数名相同,也可以使用 `this->` 来区分。
void Init(int year, int month, int day)
{
/*this->_year = year;
this->_day = month;
this->_month = day;*/
_year = year;
_day = month;
_month = day;
}
这两段代码的作用是一样的,但是我们这里的this是谁的呢?
int main()
{
Date d;
d.Init(1970, 1, 1);
return 0;
}
由这两句调用代码可以看出,是 d调用了类函数Init ,也就是说,这次函数内的this便是d的,我们可以选择直接用变量名,或者用this指针调用(与c语言的指针调用是一样的)
默认构造函数
默认构造函数是在没有显式定义构造函数时由编译器自动生成的构造函数。它没有参数,并且在创建对象时被隐式调用。当一个类没有提供任何构造函数时,编译器会自动生成一个默认构造函数。
class Date
{
public:
Date();
private:
int _year;
int _month;
int _day;
};
默认构造函数没有参数,直接使用类名,我们都不需要显示调用,只需要
Date d;//定义一个类,d
编译器就会默认调用这个构造函数,C++对于内置类型不会有处理(int, double.....)等,如果我们需要初始化的时候给数据初始化值,我们也可以手动定义默认构造函数,并在其中执行所需的初始化操作。
如果我们提供了其他类型的构造函数(非默认构造函数),但没有提供默认构造函数,编译器将不会自动生成默认构造函数。这意味着,如果需要通过不带参数的构造函数创建对象,就必须显式地提供一个默认构造函数。
Date(int year,int month,int day)
{
_year = year;
_day = month;
_month = day;
}
我们可能在自学的时候,看到,对于自定义类型的数据,编译器会自动声称默认构造函数,但是对于内置类型不会处理,其实,编译器生成的默认构造函数是取调用自定义类型的构造函数,C++的自定义类型与C语言自定义类型是不一样的,C++的自定义类型包括数据和我们自己定义的默认构造函数!
//C语言中的自定义类型
class Date
{
private:
int _year;
int _month;
int _day;
};
class Time
{
private:
int _hour;
int _minute;
int _second;
Date d;
};
int main()
{
Time t;
return 0;
}
但是我们把C++的自定义类型写完整的话
class Date
{
Date()
{
_year = 1970;
_month = 1;
_day = 1;
}
private:
int _year;
int _month;
int _day;
};
我们会发现,这时候,我们的自定义类型就被初始化了,也就是说,对于自定义类型,编译器回去调用所欲自定义类型的默认构造函数!!
析构函数
析构函数(Destructor)是在对象销毁时自动调用的特殊成员函数。它的作用是释放对象所占用的资源,进行清理操作,以确保对象在销毁时不会造成资源泄漏或产生其他问题!写出来语法与构造函数相似 就是在前面加上 ~ 号
他会自己调用,编译器判断变量是否出作用域后,会自动调用析构函数取销毁为其分配的空间,对于上面我们写的类,就没有必要写,因为他没有分配空间,不会造成内存泄漏,
class Stack
{
public:
Stack()
{
size = capacity = 10;
int* temp = (int*)malloc(sizeof(int) * capacity);//为其分配空间
date = temp;
}
~Stack()
{
free(date);
date = NULL;
}
private:
int* date;
int size;
int capacity;
};
而对于这个Stack类,他在构造的时候对齐分配了空间,我们就需要根据分配的空间,对齐进行内存回收。 也就是说 当我们构造函数对类内数据分配空间的时候就需要显式的写出相应的析构函数!
拷贝构造函数
C++中,编译器不仅会生成默认构造函数,同时还会生成拷贝构造函数,但是!默认生成的拷贝构造函数,只是进行浅拷贝:
s2与s1指向同一块空间,因为他们都分配空间,也就是说,他们任意一位出了自己的作用域就会被销毁,这也叫造成了同一块空间被free多次,而且他们还不能存储自己的数据,他们储存的数据会相同,相互影响!然后就会
程序崩溃!,这就很清楚看出来,对于需要分配空间的对象,我们就需要将构造函数,析构函数,拷贝构造函数写出来,
class Stack
{
public:
Stack()
{
size = capacity = 10;
int* temp = (int*)malloc(sizeof(int) * capacity);
date = temp;
}
~Stack()
{
cout << "~Stack()" << endl;
free(date);
date = NULL;
}
Stack(const Stack& st)//拷贝构造
{
date = (int*)malloc(sizeof(int) * st.capacity);
memcpy(date, st.date, sizeof(int) * st.capacity);
size = st.size;
capacity = st.capacity;
}
private:
int* date;
int size;
int capacity;
};
拷贝构造的函数参数,const stack& st,很讲究
1:cosnt 类st不会再函数内修改。
2:引用可以防止 Stack(Stack st)重复调用默认构造,导致程序崩溃