c++的类和对象(1)
1.类的引入
类的引入是面向对象编程(OOP)中的一个基本概念,它代表了面向对象编程范式的核心。在C++中,类(Class)是一种用户定义的数据类型,它将数据表示(属性)和操作数据的方法(函数)组合成一个单一的实体。
class ClassName {
// 类的成员变量(属性)
// 类的成员函数(方法)
public:
// 公有成员(可以被类外部访问)
protected:
// 保护成员(可以被类自身、派生类以及友元函数访问)
private:
// 私有成员(只能被类自身访问)
};
类的引入目的
- 模块化:类允许程序员将复杂的系统分解成更小、更易于管理的模块。
- 代码复用:通过继承机制,类可以继承其他类的特性,减少代码重复。
- 易于维护:由于类封装了数据和操作,因此更改类的内部实现不会影响到其他部分的代码。
- 增强安全性:通过访问控制,类可以保护其数据不被不恰当的访问和修改。
2.类的定义
类定义的组成部分
-
访问修饰符:
public
:公有成员可以被类的外部访问。protected
:保护成员可以被类自身、派生类以及友元函数访问。private
:私有成员只能被类自身访问。
-
成员变量:类中定义的数据。
-
成员函数:类中定义的操作数据的函数。
-
构造函数:用于初始化类的对象。
-
析构函数:用于在对象生命周期结束时执行清理工作。
类定义的示例
#include <string>
class Person {
private:
// 私有成员变量
std::string name;
int age;
public:
// 构造函数
Person(std::string n, int a) : name(n), age(a) {}
// 成员函数,设置姓名
void setName(std::string n) {
name = n;
}
// 成员函数,获取姓名
std::string getName() const {
return name;
}
// 成员函数,设置年龄
void setAge(int a) {
age = a;
}
// 成员函数,获取年龄
int getAge() const {
return age;
}
// 成员函数,显示个人信息
void display() const {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
}
};
3.类的封装
封装
封装是一种将数据和操作数据的函数捆绑在一起的方法,以防止外部直接访问类的内部工作细节。封装的目的包括:
- 隐藏实现细节:用户不需要知道类的内部工作方式,只需要知道如何使用它。
- 保护数据:防止外部直接修改类的内部状态,确保数据的一致性和完整性。
- 模块化:通过封装,类的内部实现可以独立于其他类的实现进行更改,而不会影响使用该类的代码。
class BankAccount {
private:
// 私有成员变量,只能被类内部访问
double balance;
public:
// 构造函数,初始化账户余额
BankAccount(double initialBalance) : balance(initialBalance) {}
// 公有成员函数,用于存钱
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
// 公有成员函数,用于取钱
bool withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
return true;
} else {
return false;
}
}
// 公有成员函数,获取账户余额
double getBalance() const {
return balance;
}
};
int main() {
BankAccount account(1000.0); // 创建账户,初始余额为1000
account.deposit(500.0); // 存入500
account.withdraw(200.0); // 取出200
// 尝试直接访问私有成员变量(错误,编译器将报错)
// account.balance = 0;
std::cout << "Current balance: " << account.getBalance() << std::endl; // 正确地获取余额
return 0;
}
4.类的作用域
在C++中,类定义了一个新的作用域,这意味着在类内部声明的名称(如成员变量和成员函数)在类的外部是不可见的,除非通过类的成员访问运算符(.
)或者指针/引用的箭头运算符(->
)来访问。以下是关于类作用域的一些关键点:
类作用域的特点:
-
成员变量和成员函数:
- 在类内部声明的成员变量和成员函数属于类的作用域。
- 成员变量和成员函数默认是私有的,除非明确指定为
public
或protected
。
-
名称隐藏:
- 类内部的名称会隐藏外部的同名名称。如果在类内部声明了一个与外部同名的变量或函数,那么在类的作用域内,这个内部名称会覆盖外部名称。
-
作用域解析运算符:
- 如果需要在类外部访问类的成员,需要使用作用域解析运算符
::
。
- 如果需要在类外部访问类的成员,需要使用作用域解析运算符
-
构造函数和析构函数:
- 构造函数和析构函数也属于类的作用域,它们在创建和销毁类的对象时被调用。
class MyClass {
public:
int publicVar; // 公有成员变量
void publicFunc() { // 公有成员函数
// 在这里可以访问类的所有成员
}
private:
int privateVar; // 私有成员变量
void privateFunc() { // 私有成员函数
// 在这里可以访问类的所有成员
}
// 构造函数
MyClass() {
privateVar = 0;
}
};
int main() {
MyClass obj;
// 访问公有成员
obj.publicVar = 10; // 正确
obj.publicFunc(); // 正确
// 尝试访问私有成员(错误,编译器将报错)
// obj.privateVar = 20; // 错误
// obj.privateFunc(); // 错误
return 0;
}
类的对象大小的计算
在C++中,类对象的大小取决于其成员变量的大小、对齐要求以及可能的填充(padding)字节。以下是计算类对象大小的几个关键点:
-
成员变量:类对象的大小至少是其所有非静态成员变量大小的总和。
-
成员对齐:每个成员变量可能有其特定的对齐要求。对象的内存布局会按照这些对齐要求来调整。
-
类对齐:整个类对象可能也有一个对齐要求,这会影响对象的总大小。
-
填充字节:编译器可能会在成员变量之间插入填充字节以满足对齐要求。
class MyClass {
int a; // 通常占用4字节
char b; // 占用1字节
double c; // 通常占用8字节
// 可能的填充字节
};
// 假设默认对齐数为8
// 计算过程:
// a占用4字节,b占用1字节,但由于double需要对齐到8字节,所以b之后会有3字节的填充
// c占用8字节
// 因此,MyClass对象的总大小为:4 + 1 + 3(填充) + 8 = 16字节
类成员函数的this指针
在C++中,每个非静态成员函数都有一个隐含的参数,称为this
指针。this
指针指向调用该成员函数的对象本身。以下是关于this
指针的一些关键点:
-
隐含参数:
this
指针是隐含传递给成员函数的第一个参数。 -
类型:
this
指针的类型是类类型的指针,例如MyClass*
。 -
用途:
this
指针用于访问调用该函数的对象的成员变量和成员函数。 -
非静态成员函数:只有非静态成员函数才有
this
指针。 -
默认值:在成员函数内部,可以显式地使用
this
指针,也可以省略它,因为编译器会自动添加。
class MyClass {
public:
int value;
void setValue(int val) {
this->value = val; // 使用this指针显式地设置成员变量
}
int getValue() const {
return this->value; // 使用this指针显式地获取成员变量
}
};
MyClass obj;
obj.setValue(10); // 调用时,编译器会隐含传递this指针
int val = obj.getValue(); // 同样,编译器会隐含传递this指针