- C++ 中面向对象编程实现数据隐藏的方式
- 访问控制修饰符
- private 关键字:将类的成员(数据成员和成员函数)声明为
private
,这些成员只能在类的内部被访问。例如,有一个BankAccount
类,账户余额balance
是敏感信息,应该隐藏起来。
class BankAccount {
private:
double balance;
public:
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
void withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
}
}
double getBalance() {
return balance;
}
};
- 原理和优势:在这个例子中,
balance
被声明为private
。外部代码不能直接访问balance
,只能通过deposit
、withdraw
和getBalance
这些公共成员函数来间接操作和获取账户余额。这样就可以在这些函数中添加各种验证逻辑,比如存款金额不能为负数、取款金额不能超过余额等,保证数据的完整性和安全性。
- protected 关键字(用于继承场景):在有继承关系的类层次结构中,
protected
成员可以在派生类中访问,但对于外部类来说是不可见的。例如,有一个基类Shape
和派生类Circle
。
class Shape {
protected:
int color;
public:
Shape(int c) : color(c) {}
};
class Circle : public Shape {
public:
Circle(int c) : Shape(c) {}
void printColor() {
std::cout << "Color of the circle: " << color << std::endl;
}
};
- 原理和优势:在
Shape
类中,color
被声明为protected
。它不能被外部代码直接访问,但在派生类Circle
中可以访问。这种机制允许在继承层次结构中,基类将一些成员开放给派生类,让派生类能够在一定程度上共享基类的内部状态,同时又限制了外部对这些成员的访问。
- 信息隐藏的设计理念:除了使用访问控制修饰符,在设计类时,应该遵循信息隐藏的原则。只暴露必要的接口给外部,隐藏类的内部实现细节。例如,对于一个
Stack
类,用户只需要知道push
、pop
、top
等操作接口,而不需要了解栈是用数组还是链表实现的。
class Stack {
private:
std::vector<int> data;
public:
void push(int value) {
data.push_back(value);
}
int pop() {
if (!data.empty()) {
int topValue = data.back();
data.pop_back();
return topValue;
}
throw std::runtime_error("Stack is empty");
}
int top() {
if (!data.empty()) {
return data.back();
}
throw std::runtime_error("Stack is empty");
}
};
- 原理和优势:这里
Stack
类内部使用std::vector
来存储数据,这是实现细节。外部用户只通过push
、pop
和top
接口来使用栈,即使将来修改Stack
的内部存储结构(比如改为链表),只要接口保持不变,使用Stack
的其他代码不需要修改。
- C++ 中面向对象编程处理异常的方法
- try - catch 块
- 基本语法和流程:
try
块中放置可能抛出异常的代码,catch
块用于捕获并处理异常。例如,在一个除法运算函数中,除数不能为零。
double divide(double dividend, double divisor) {
try {
if (divisor == 0) {
throw std::runtime_error("Divisor cannot be zero");
}
return dividend / divisor;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 0;
}
}
- 原理和优势:当
divisor
为 0 时,会抛出一个std::runtime_error
类型的异常。程序流程会立即跳转到catch
块,catch
块捕获到这个异常后,可以进行相应的错误处理,比如打印错误信息并返回一个默认值。这样可以避免程序因为异常而崩溃,使程序更加健壮。
- 异常类型层次结构:C++ 标准库有一个异常类型层次结构,
std::exception
是所有标准异常的基类。派生类包括std::runtime_error
、std::logic_error
等。可以根据不同的异常类型进行不同的处理。例如,有一个函数可能抛出两种不同类型的异常。
void someFunction() {
try {
// 可能抛出std::runtime_error或std::logic_error的代码
} catch (const std::runtime_error& e) {
std::cerr << "Runtime error: " << e.what() << std::endl;
} catch (const std::logic_error& e) {
std::cerr << "Logic error: " << e.what() << std::endl;
}
}
- 原理和优势:通过这种方式,可以针对不同类型的异常进行精细的处理。比如,对于
runtime_error
可能是由于运行时环境问题(如文件不存在、网络连接中断等),而logic_error
可能是程序逻辑错误(如参数无效等),可以分别采取不同的恢复策略或者错误提示。
- 自定义异常类:除了使用标准异常类,还可以自定义异常类来更好地适应具体的应用场景。例如,在一个学生成绩管理系统中,定义一个
InvalidGradeException
。
class InvalidGradeException : public std::runtime_error {
public:
InvalidGradeException(const std::string& message) : std::runtime_error(message) {}
};
class StudentGrades {
public:
void setGrade(int grade) {
if (grade < 0 || grade > 100) {
throw InvalidGradeException("Grade must be between 0 and 100");
}
// 设置成绩的代码
}
};
- 原理和优势:当
setGrade
函数接收到无效的成绩时,会抛出InvalidGradeException
。这种自定义异常类可以携带更具体的错误信息,并且可以在catch
块中根据自定义异常类型进行专门的处理,使得异常处理更加贴合实际业务逻辑。同时,继承自std::runtime_error
(或其他标准异常基类)可以利用标准库的异常处理机制,如what()
函数来获取错误信息。