类和对象
在 C++ 中,类和对象是面向对象编程(OOP)的核心概念。类是一个用户定义的数据类型,它封装了数据和对数据的操作。对象则是类的实例。
类的定义
定义一个类需要使用关键字classs
,然后指定类的名称,并且类的猪蹄是包含在一对花括号中,主体包含类的成员变量和成员函数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Car {
public:
// 公有成员函数
void setBrand(std::string b) {
brand = b;
}
void setModel(std::string m) {
model = m;
}
void displayInfo() {
std::cout << "Brand: " << brand << ", Model: " << model << std::endl;
}
private:
// 私有成员变量
std::string brand;
std::string model;
};
对象的创建
创建一个类的对象并使用它的方法:1
2
3
4
5
6
7
8
9
10int main() {
Car myCar; // 创建对象
myCar.setBrand("Toyota");
myCar.setModel("Corolla");
myCar.displayInfo(); // 输出 Brand: Toyota, Model: Corolla
return 0;
}
类访问修饰符
关键字 public、private、protected
称为访问修饰符。
一个类可以有多个public、protected 或 private
标记区域。每个标记区域在下一个标记区域开始之前或者在遇到类主体结束右括号之前都是有效的。成员和类的默认访问修饰符是 private
。
- public 公有成员
- 公有成员在程序中类的外部是可访问的。可以不使用任何成员函数来设置和获取公有变量的值。
- 公有成员通常是类的接口的一部分。
- private私有成员:
- 私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类的成员函数和友元函数可以访问私有成员。
- 默认情况下,类的所有成员都是私有的。
- 实际操作中,我们一般会在私有区域定义数据,在公有区域定义相关的函数,以便在类的外部也可以调用这些函数。
- 私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类的成员函数和友元函数可以访问私有成员。
- protected受保护成员:
- protected(受保护)成员变量或函数与私有成员十分相似,但有一点不同,protected(受保护)成员在派生类(即子类)中是可访问的。
- 保护成员适用于需要在继承结构中共享但不希望被外部代码访问的数据。
指向类的指针
指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->
,就像访问指向结构的指针一样。与所有的指针一样,您必须在使用指针之前,对指针进行初始化。
在 C++ 中,指向类的指针指向一个类的对象,与普通的指针相似,指向类的指针可以用于访问对象的成员变量和成员函数。
指向类的指针还可以用于动态分配内存,创建类的对象。
指向类的指针可以作为函数参数传递。
this指针
- 在 C++ 中,
this
指针是一个特殊的指针,它指向当前对象的实例。 - 在 C++ 中,每一个对象都能通过
this
指针来访问自己的地址。 this
是一个隐藏的指针,可以在类的成员函数中使用,它可以用来指向调用对象。- 当一个对象的成员函数被调用时,编译器会隐式地传递该对象的地址作为
this
指针。 - 友元函数没有
this
指针,因为友元不是类的成员,只有成员函数才有this
指针。
理解:
当我们调用成员函数时,实际上是替某个对象调用它。
成员函数通过一个名为 this
的额外隐式参数来访问调用它的那个对象,当我们调用一个成员函数时,用请求该函数的对象地址初始化 this
。
当你进入一个房子后,
你可以看见桌子、椅子、地板等,
但是房子你是看不到全貌了。
对于一个类的实例来说,
你可以看到它的成员函数、成员变量,
但是实例本身呢?
this是一个指针,
它时时刻刻指向你这个实例本身。
类的静态成员
可以使用 static
关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。
静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化。
静态成员函数:如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。
静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。
静态成员函数有一个类范围,他们不能访问类的 this
指针。
类的常量成员:
类的常量成员 通常是指被声明为 const 类型的类成员变量。const
成员变量的值一旦被初始化,就不能再修改。类的常量成员变量有助于确保类的某些数据成员在对象的生命周期内保持不变。
- 由于常量成员变量不能修改,所以必须在构造函数的初始化列表中进行初始化。
1
2
3
4
5
6class ClassName {
private:
const int constantValue; // 常量成员变量
public:
ClassName(int val) : constantValue(val) {} // 初始化常量成员变量
}; - 常量静态成员变量
类的静态常量成员变量也是常见的做法,尤其是在你希望为所有对象共享一个常量时。静态成员变量属于类本身,而不是类的某个特定对象。静态常量成员变量常常用于定义类的一些不变的全局常量,例如数学常数或最大值。1
2
3
4
5
6class ClassName {
private:
static const int constantValue = 10; // 静态常量成员变量
public:
static int getConstantValue() { return constantValue; }
}; - mutable 关键字与常量成员变量的关系
mutable
关键字使得类的某个成员变量可以在常量成员函数中修改。虽然常量成员函数承诺不修改对象的状态,但如果某个成员变量是mutable
,它就可以在常量成员函数中被修改。这对于一些需要在不改变对象外部状态的情况下进行内部更新的场景非常有用。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using namespace std;
class MyClass {
private:
mutable int cache; // 可以在 const 函数中修改
public:
MyClass() : cache(0) {}
// 常量成员函数,可以修改 mutable 成员变量
void updateCache() const {
cache++; // 即使是常量函数,也可以修改 mutable 成员
}
void showCache() const {
cout << "Cache: " << cache << endl;
}
};
int main() {
MyClass obj;
obj.showCache(); // 输出 Cache: 0
obj.updateCache(); // 更新 cache
obj.showCache(); // 输出 Cache: 1
return 0;
} - 常量成员函数与常量成员变量
常量成员变量通常与常量成员函数一起使用,以确保对象的状态在函数调用期间保持不变。常量成员函数通常用于访问这些常量成员变量,而不会修改它们。
抽象类
在 C++ 中,抽象类 是一种不能直接实例化的类,通常用于定义接口或者基础类。抽象类通过包含纯虚函数(pure virtual function)来实现,目的是让派生类提供具体实现。
抽象类特点
- 不能实例化:因为抽象类的纯虚函数没有实现。
- 可以有构造函数和析构函数:尽管不能实例化,抽象类可以用于构造派生类对象时初始化其基类部分。
- 可以包含普通成员函数:抽象类可以实现部分功能,让派生类复用。
- 派生类必须实现所有纯虚函数:除非派生类也定义为抽象类。
友元类
在 C++ 中,友元类(friend class) 是允许另一个类访问其私有成员和保护成员的类。通过友元机制,两个类之间可以共享私有数据,增加类之间的协作性,但也需要注意使用的场景,以免破坏封装性。
通过在类的定义中使用 friend class 关键字,可以将另一个类声明为友元类。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class B; // 前向声明
class A {
private:
int secretData; // 私有成员
public:
A(int value) : secretData(value) {}
// 声明 B 是 A 的友元类
friend class B;
};
class B {
public:
void accessA(const A& a) {
// 直接访问 A 的私有成员
std::cout << "Accessing A's private data: " << a.secretData << std::endl;
}
};