类可以被认为是一种模板,它描述了如何创建对象,对象是类的实例。
类定义了对象的数据(称为属性)和可以对这些数据进行的操作(称为方法或函数)
class Student {
public:
string name; // 学生的名字
int age; // 学生的年龄
double score; // 学生成绩
// 学习的方法
void study() {
cout << name << " is studying." << endl;
}
// 考试的方法
void takeExam() {
cout << name << " is taking an exam." << endl;
}
};
在 C++ 中,一个类可以从另一个类那里继承属性和方法。这个功能非常强大,因为它让我们能够创建更专门化的类,而无需重新编写所有的代码。
例如,我们可以创建一个“大学生”类,它继承了“学生”类的所有属性和方法,并添加了一些新的:
class UniversityStudent : public Student {
public:
string major; // 学习的专业
// 参加实验的方法
void doExperiment() {
cout << name << " is doing an experiment." << endl;
}
};
多态性则是指一个接口可以有多种实现方式,或者说,一个基类的指针可以指向其子类的对象。这是一种强大的特性,因为它让我们能够编写更一般、更灵活的代码。
Student *s = new UniversityStudent();
s->study(); // 调用的是 UniversityStudent 的 study 方法,如果该方法被重写的话
构造函数是一种特殊的成员函数,当创建类的新对象时,它会自动调用。构造函数的名称与类的名称相同,没有返回类型。你可以使用构造函数来初始化对象的数据成员。
class MyClass {
public:
int x;
// 构造函数
MyClass(int value) : x(value) {}
};
int main() {
MyClass obj(10); // 创建对象并初始化x为10
return 0;
}
析构函数也是一种特殊的成员函数,但是当对象被销毁(即,它离开其作用范围或者被删除)时,它会被自动调用。析构函数的名称是类的名称前加上波浪符(~),没有参数和返回类型。你可以在析构函数中完成任何必要的清理工作。
class MyClass {
public:
// 析构函数
~MyClass() {
cout << "Object is being destroyed." << endl;
}
};
int main() {
MyClass obj; // 当obj离开其作用范围时,将调用析构函数
return 0;
}
class MyClass {
public:
MyClass(int value) {
cout << "Constructor called with value: " << value << endl;
}
MyClass() {
cout << "Default constructor called." << endl;
}
};
int main() {
MyClass obj1(10); // 调用带参数的构造函数
MyClass obj2; // 调用无参数的构造函数
return 0;
}
在创建对象时,可以直接为其成员变量赋值。但在创建对象后给其成员变量赋值,和在创建对象时就给其成员函数赋值,二者是有区别的。前者涉及两步操作(先创建,后付制),后者在创建的同时,就为其成员变量赋值,只有一步操作。
class MyClass {
public:
int x;
};
int main() {
MyClass obj1;
obj1.x = 10; // 先创建对象,然后给x赋值
MyClass obj2 = {20}; // 创建对象的同时给x赋值
return 0;
}
使用new创建对象时,会调用构造函数。使用delete销毁对象时,会调用析构函数。
class MyClass {
public:
MyClass() {
cout << "Constructor called." << endl;
}
~MyClass() {
cout << "Destructor called." << endl;
}
};
int main() {
MyClass* obj = new MyClass; // 创建对象,调用构造函数
delete obj; // 销毁对象,调用析构函数
return 0;
}
如果一个类的成员是另一个类的对象,那么在创建对象时,会先构造成员类。在销毁对象时,会先析构成员类。
class MemberClass {
public:
MemberClass() {
cout << "MemberClass constructor called." << endl;
}
~MemberClass() {
cout << "MemberClass destructor called." << endl;
}
};
class MyClass {
public:
MemberClass member;
MyClass() {
cout << "MyClass constructor called." << endl;
}
~MyClass() {
cout << "MyClass destructor called." << endl;
}
};
int main() {
MyClass obj; // 创建对象
// 当obj离开其作用范围时,析构函数被调用
return 0;
}
运行结果:
MemberClass constructor called.
MyClass constructor called.
MyClass destructor called.
MemberClass destructor called.
在C++中,我们可以不在提供对象名的情况下创建一个对象,这种对象我们称之为匿名对象(也称临时对象)。
当你写出类名并跟上圆括号以及参数时,你实际上时在创建一个匿名对象。这看起来像是在调用构造函数,但实际上是创建了一个匿名对象。
class MyClass {
public:
MyClass(int value) {
cout << "Constructor called with value: " << value << endl;
}
};
int main() {
MyClass(10); // 创建一个匿名对象,输出 "Constructor called with value: 10"
return 0;
}
MyClass(10);
这一行创建了一个匿名对象。这个对象没有名称,而且它在创建后立即被销毁。因此,你只能在创建它的那一行代码中使用它。你应该注意,匿名对象在创建后会立即被销毁,所以你不能在后续的代码中引用它。
匿名对象在许多情况下都很有用。例如,你可以在一个函数返回对象时使用匿名对象,或者在将对象作为函数参数时使用匿名对象。
C++的拷贝构造函数用于通过复制现有的对象来创建新的对象。如果在类中未定义复制构造函数,编译器会自动提供一个,用于复制现有对象的成员变量。
定义拷贝构造函数的语法如下:
ClassName(const ClassName& objectName){...};
创建新的对象的语法如下:
ClassName newObjectName(existingObjectName);
ClassName newObjectName = existingObjectName;
在使用拷贝构造函数时,需要注意以下几点:
例:
#include
using namespace std;
class Point {
private:
int x, y;
public:
// 构造函数
Point(int x1, int y1) {
x = x1;
y = y1;
}
// 拷贝构造函数
Point(const Point& p2) {
x = p2.x;
y = p2.y;
}
int getX() {
return x;
}
int getY() {
return y;
}
};
int main() {
Point p1(10, 15); // Normal constructor is called here
Point p2 = p1; // Copy constructor is called here
// Let us access values assigned by constructors
cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY();
cout << "\np2.x = " << p2.getX() << ", p2.y = " << p2.getY();
return 0;
}
浅拷贝和深拷贝是两种不同的对象复制方法,适用于处理类中包含动态分配的内存或指针的情况。
浅拷贝是指当一个类包含指针变量,并使用默认的拷贝构造函数进行复制时,只会复制指针而不会复制指针所指向的内容。这会导致被复制的对象和原始对象中的指针指向相同的内存地址,修改其中一个对象的数据可能会影响另一个对象的数据,这种现象称为别名问题。更糟糕的是,如果其中一个对象被析构,可能会释放掉共享的内存,导致另一个对象的指针变为悬挂指针(野指针),这是浅拷贝的主要问题。
深拷贝是指在拷贝对象时,不仅复制指针变量,而且复制指针所指向的数据。通过深拷贝,我们可以创建一个与原始对象数据相同但完全独立的新对象,修改其中一个对象的数据不会影响另一个对象的数据。实现深拷贝通常需要我们自定义拷贝构造函数。
#include
using namespace std;
class MyClass {
private:
int* data;
public:
MyClass(int value) {
data = new int;
*data = value;
}
// 浅拷贝
MyClass(const MyClass& source) {
data = source.data;
}
// 深拷贝
MyClass deepCopy(const MyClass& source) {
MyClass newObj(*source.data); // 使用传入源对象的数据值来创建新对象
return newObj; // 返回深拷贝对象
}
void setValue(int value) {
*data = value;
}
int getValue() {
return *data;
}
~MyClass() {
delete data;
}
};
int main() {
MyClass obj1(10);
MyClass obj2 = obj1; // 浅拷贝
obj1.setValue(20);
cout << "After setting obj1 to 20: \n";
cout << "obj1 value: " << obj1.getValue() << "\n"; // 输出20
cout << "obj2 value: " << obj2.getValue() << "\n"; // 也输出20,因为obj1和obj2共享内存
MyClass obj3 = obj1.deepCopy(obj1); // 深拷贝
obj1.setValue(30);
cout << "After setting obj1 to 30: \n";
cout << "obj1 value: " << obj1.getValue() << "\n"; // 输出30
cout << "obj3 value: " << obj3.getValue() << "\n"; // 输出20,因为obj1和obj3的数据是独立的
return 0;
}
在成员函数后面加上const
关键字,**这样做的目的是让编译器帮助我们确保这个函数不会修改对象的任何成员变量。**这可以在编译期间预防一类常见的错误:意外地改变对象状态。
一个const
成员函数可以被const
对象或非const
对象调用
const对象只能调用const修饰的成员函数,不能调用非cosnt修饰的成员函数。
mutable
可以突破const
的限制,被mutable
修饰的成员变量,将永远处于可变的状态,在const
修饰的函数中,mutable
成员也可以被修改。
注意:
const
时,我们告诉编译器和其他程序员,这个函数不会改变对象的状态,这在编程中很有用,因为它可以让我们更好地理解和预测代码的行为。const
函数中被修改,但不会改变对象的"逻辑状态"。在这种情况下,我们可以使用mutable
关键字来允许这种修改。比如,我们可能有一个类的成员变量用来缓存某个计算结果,虽然这个变量在const
函数中可能被改变,但是它不改变对象的逻辑状态。const
和mutable
的使用主要是提供了额外的编程约束,可以帮助我们更好地设计代码。通过使用这些关键字,我们可以将部分逻辑检查问题交给编译器,这可以减轻我们的编程负担,同时提高代码的可维护性和可读性。"this"指针是一个内置的指针,在C++类的非静态成员函数中,它被用来指向调用对象。换句话说,每个对象都可以通过"this"指针来访问自己的地址。
"this"指针的几个重要的应用包括:
如果在成员函数的括号后面使用const,意味着该成员函数不会修改对象的状态。因此,你不能通过"this"指针在该函数中修改成员变量。这是一种很好的方式来保证函数不会修改对象的状态,保证数据的一致性。
静态成员是一种特殊的成员,包括静态成员变量和静态成员函数。
静态成员为类的所有对象所共享,而不是为每个对象单独存在一份。它们在程序的生命周期中存在,存在静态存储区。
注意点:
static
关键字声明,它在类的所有对象中是共享的。静态成员变量不会在创建对象时初始化,而需要在全局区明确地初始化。静态成员变量可以通过类名加范围解析运算符(::
)访问,无需创建对象。static
关键字声明,只能访问静态成员,不能访问非静态成员。静态成员函数中没有this
指针。在非静态成员函数中,可以访问静态成员。class MyClass {
public:
static int static_value; // 静态成员变量
static void staticFunction() { // 静态成员函数
// this->value; // 错误,静态成员函数中没有this指针
static_value = 100; // 在静态成员函数中,可以访问静态成员
}
};
// 在全局区初始化静态成员变量
int MyClass::static_value = 0;
int main() {
MyClass::staticFunction(); // 使用类名加范围解析运算符(::)访问静态成员函数
std::cout << MyClass::static_value << std::endl; // 输出:100
return 0;
}
C++中有两种数据成员:nonstatic
,static
,和三种成员函数:nonstatic
,static
,virtual
。