C++:类的定义与声明、类对象应用
1. 类的声明(Class Declaration)
类的声明是向编译器介绍类的名称以及类包含哪些成员(数据成员和成员函数),但此时并不定义这些成员函数的具体实现。
语法格式:
class ClassName { public: // 公有成员(数据成员或成员函数)声明 DataType memberVariable; ReturnType memberFunction(Parameters); protected: // 保护成员声明 DataType protectedVariable; ReturnType protectedFunction(Parameters); private: // 私有成员声明 DataType privateVariable; ReturnType privateFunction(Parameters); };
例如,声明一个简单的表示二维点的类:
class Point { public: int x; int y; void printPoint(); };
在这个声明中,Point是类名,x和y是公有数据成员,表示点的横纵坐标,printPoint是公有成员函数,用于打印点的坐标。但是这里并没有给出printPoint函数的具体实现。
2. 类的定义(Class Definition)
类的定义包含了类的声明以及类成员函数的具体实现。
对于上面声明的Point类,可以这样定义其成员函数:
// 类外定义成员函数 void Point::printPoint() { std::cout << "(" << x << ", " << y << ")" << std::cout; }
如果要在类内部直接定义成员函数,语法如下(以重新定义Point类为例):
class Point { public: int x; int y; // 在类内部直接定义成员函数 void printPoint() { std::cout << "(" << x << ", " << y << ")" << std::cout; } };
类的定义可以将类的声明和部分或全部成员函数的定义放在一起,也可以先声明类,然后在类外定义成员函数。类外定义成员函数时,需要使用类名::函数名的形式来表明这个函数是属于该类的。这种方式有助于组织代码结构,提高代码的可读性和可维护性。
类是C++中的一种用户自定义类型,通过类的定义和声明,可以创建具有特定属性(数据成员)和行为(成员函数)的对象,从而实现面向对象编程中的封装、继承和多态等特性。
3. 类的成员(Class Members)
数据成员(Data Members)
数据成员用于存储类对象的属性信息。它们可以是各种基本数据类型(如int、double、char等),也可以是其他自定义类型(如其他类类型、结构体类型、枚举类型等)。
例如,在一个表示矩形的类Rectangle中:
class Rectangle { public: int length; int width; };
这里的length和width就是Rectangle类的数据成员,用于存储矩形的长和宽。数据成员可以根据访问权限(public、protected、private)被类内部或外部的代码访问和操作。
成员函数(Member Functions)
成员函数定义了类对象的行为,即类对象可以执行的操作。成员函数可以访问和操作类的数据成员,以实现特定的功能。
继续以Rectangle类为例,我们可以添加一个计算矩形面积的成员函数:
class Rectangle { public: int length; int width; int area() { return length * width; } };
在这个例子中,area就是Rectangle类的成员函数,它通过访问length和width数据成员来计算并返回矩形的面积。
构造函数(Constructors)
构造函数是一种特殊的成员函数,用于在创建类对象时进行初始化操作。它的名字与类名相同,没有返回类型(包括void类型也不能有)。
例如,对于Rectangle类,我们可以定义一个构造函数:
class Rectangle { public: int length; int width; Rectangle(int l, int w) { length = l; width = w; } };
默认构造函数(Default Constructor)
如果类中没有定义任何构造函数,编译器会自动生成一个默认构造函数。这个默认构造函数不接受任何参数,并且会对类中的数据成员进行默认初始化(对于基本数据类型,如int,初始化为0或者不确定的值;对于类类型成员,会调用其默认构造函数)。
如果类中定义了自己的构造函数(无论是带参数的还是不带参数的),编译器就不会再自动生成默认构造函数。如果还需要默认构造函数,就需要自己显式定义。例如:
class Rectangle { public: int length; int width; Rectangle() { length = 0; width = 0; } Rectangle(int l, int w) { length = l; width = w; } };
初始化列表(Initialization List)
在构造函数中,可以使用初始化列表来初始化数据成员,这种方式通常比在构造函数体内部赋值更高效。例如:
class Rectangle { public: int length; int width; Rectangle(int l, int w) : length(l), width(w) {} };
这里的length(l)和width(w)就是初始化列表,它直接在对象创建时初始化数据成员。
4. 成员函数的特性
访问控制(Access Control)
成员函数可以根据其声明所在的访问控制区域(public、protected、private)来决定是否能被类外部的代码调用。
public成员函数可以被类外部的任何代码调用;protected成员函数只能被类内部以及派生类中的代码调用;private成员函数只能被类内部的代码调用。
隐含的this指针
在成员函数内部,有一个隐含的this指针,它指向调用该成员函数的对象。例如,在Rectangle类的area函数中,实际上this->length和this->width分别指向调用area函数的那个Rectangle对象的length和width数据成员。不过在实际编写代码时,通常不需要显式地使用this指针(除非存在命名冲突等特殊情况)。
重载(Overloading)
成员函数可以像普通函数一样进行重载,即同一个类中可以有多个同名但参数列表不同(参数个数、类型或者顺序不同)的成员函数。例如:
class Rectangle { public: int length; int width; Rectangle() {} Rectangle(int l, int w); Rectangle(int side); };
这里定义了三个Rectangle类的构造函数,它们相互重载,可以根据不同的参数情况来创建Rectangle对象。
5. 类对象
在 C++中,类对象是根据类定义创建的具体实体。
(1). 创建类对象
可以通过以下方式创建类对象:
直接定义对象:
class MyClass { public: int data; void display() { std::cout << "Data: " << data << std::endl; } }; int main() { MyClass obj; // 创建一个 MyClass 类的对象 obj.data = 10; obj.display(); return 0; }
使用动态内存分配(new 操作符):
class MyClass { public: int data; void display() { std::cout << "Data: " << data << std::endl; } }; int main() { MyClass* ptr = new MyClass(); // 使用 new 创建动态对象 ptr->data = 20; ptr->display(); delete ptr; // 释放动态分配的内存 return 0; }
(2). 访问类对象的成员
可以通过点运算符(.)和箭头运算符(->)来访问类对象的成员:
点运算符用于访问非指针类型的对象成员:
MyClass obj; obj.data = 30; obj.display();
箭头运算符用于访问指针类型的对象成员:
MyClass* ptr = new MyClass(); ptr->data = 40; ptr->display();
(3). 类对象的生命周期
局部对象:在函数内部定义的对象,其生命周期从定义处开始,到函数结束时结束。
全局对象:在所有函数外部定义的对象,其生命周期从程序开始执行时开始,到程序结束时结束。
动态分配的对象:使用 new 操作符创建的对象,其生命周期由程序员通过 delete 操作符来控制。如果不及时释放动态分配的对象,会导致内存泄漏。
(4). 类对象作为函数参数和返回值
作为函数参数:可以将类对象作为函数的参数传递,有传值、传引用和传指针三种方式。
传值:会创建一个对象的副本,可能会涉及到对象的拷贝构造函数的调用,开销较大。
void processObject(MyClass obj) { // 对传入的对象进行处理 }
传引用:避免了对象的拷贝,提高了效率。
void processObject(MyClass& obj) { // 对传入的引用对象进行处理 }
传指针:也可以避免对象的拷贝,并且可以通过指针修改对象的值。
void processObject(MyClass* obj) { // 对传入的指针指向的对象进行处理 }
作为函数返回值:函数可以返回一个类对象。如果返回值是对象,可能会涉及到对象的拷贝构造函数和析构函数的调用。如果返回值是引用,则不会进行拷贝。
MyClass createObject() { MyClass obj; return obj; } MyClass& createObjectRef() { static MyClass obj; return obj; }
(5). 类对象的初始化和赋值
初始化:可以在定义对象时使用初始化列表或构造函数来初始化对象的成员。
MyClass obj1 = {10}; // 使用初始化列表初始化对象 MyClass obj2(20); // 使用构造函数初始化对象
赋值:可以使用赋值运算符(=)将一个对象的值赋给另一个对象。如果类中没有定义赋值运算符,编译器会自动生成一个默认的赋值运算符。
MyClass obj1, obj2; obj1.data = 30; obj2 = obj1; // 将 obj1 的值赋给 obj2
类对象是 C++面向对象编程的核心概念之一,通过类对象可以封装数据和操作,实现代码的模块化和可维护性。
(6). C++类的定义与类对象的应用
以下是一个 C++类的完整应用例子,包括一个简单的学生类:
#include <iostream> #include <string> class Student { private: std::string name; int age; double grade; public: // 构造函数 Student(std::string n, int a, double g) : name(n), age(a), grade(g) {} // 获取学生姓名的成员函数 std::string getName() const { return name; } // 获取学生年龄的成员函数 int getAge() const { return age; } // 获取学生成绩的成员函数 double getGrade() const { return grade; } // 设置学生成绩的成员函数 void setGrade(double newGrade) { grade = newGrade; } // 打印学生信息的成员函数 void printInfo() const { std::cout << "Name: " << name << ", Age: " << age << ", Grade: " << grade << std::endl; } }; int main() { // 创建一个学生对象 Student s1("Alice", 18, 90.5); s1.printInfo(); // 修改学生成绩并再次打印信息 s1.setGrade(95.0); s1.printInfo(); return 0; }
在这个例子中,Student类有三个私有数据成员name、age和grade,分别表示学生的姓名、年龄和成绩。类中还定义了构造函数和一系列成员函数来操作这些数据成员,包括获取和设置学生信息的函数以及打印学生信息的函数。在main函数中,创建了一个Student对象,并通过成员函数对其进行操作和打印信息。