c++的类和对象(3)
1. Static成员
静态成员是类的一部分,但它们与类的任何特定对象无关。静态成员分为静态数据成员和静态成员函数。
静态数据成员
静态数据成员是与类关联的数据,它们在类的所有对象之间共享。这意味着无论创建了多少个类的对象,静态数据成员只有一个副本。静态数据成员在类的所有实例之外独立存在,并且在程序开始执行之前就已经存在。
以下是静态数据成员的一些特点:
- 静态数据成员在类声明中使用
static
关键字进行声明。 - 静态数据成员必须在类的外部进行定义和初始化。
- 静态数据成员可以通过类名和作用域解析运算符
::
直接访问,无需创建类的对象。
#include <iostream>
class MyClass {
public:
static int staticData; // 声明静态数据成员
};
// 定义并初始化静态数据成员
int MyClass::staticData = 10;
int main() {
// 通过类名直接访问静态数据成员
std::cout << "Static data member value: " << MyClass::staticData << std::endl;
// 通过对象访问静态数据成员(不推荐,但可行)
MyClass obj;
std::cout << "Static data member value via object: " << obj.staticData << std::endl;
return 0;
}
静态成员函数
静态成员函数与静态数据成员类似,它们也是类的一部分,但不与类的任何特定对象关联。静态成员函数可以直接访问静态数据成员,但不能直接访问非静态成员,因为它们没有 this
指针。
以下是静态成员函数的一些特点:
- 静态成员函数在类声明中使用
static
关键字进行声明。 - 静态成员函数可以在类内部或外部定义。
- 静态成员函数可以通过类名和作用域解析运算符
::
直接调用,无需创建类的对象。
#include <iostream>
class MyClass {
public:
static int staticData; // 声明静态数据成员
// 声明静态成员函数
static void staticFunction() {
std::cout << "Static function called. Static data member value: " << staticData << std::endl;
}
};
int MyClass::staticData = 10;
int main() {
// 通过类名直接调用静态成员函数
MyClass::staticFunction();
// 通过对象调用静态成员函数(不推荐,但可行)
MyClass obj;
obj.staticFunction();
return 0;
}
2. 友元
在C++中,友元(Friend)是一种特殊的访问权限,它允许一个类将其非公有成员(私有或保护)暴露给特定的函数或另一个类,而无需通过类的公有接口。友元可以是函数、类或类的成员函数。
友元函数
友元函数可以是一个普通的非成员函数,也可以是另一个类的成员函数。友元函数不是当前类的成员,但它可以访问该类的所有成员,包括私有成员。
class MyClass {
private:
int secretValue;
public:
MyClass() : secretValue(0) {}
// 声明一个友元函数
friend void friendFunction(MyClass& obj);
};
// 定义友元函数
void friendFunction(MyClass& obj) {
// 直接访问MyClass的私有成员
obj.secretValue = 42;
}
友元类
友元类是指一个类被声明为另一个类的友元,这样友元类的所有成员函数都可以访问另一个类的所有成员,包括私有成员。
class AnotherClass;
class MyClass {
private:
int secretValue;
public:
MyClass() : secretValue(0) {}
// 声明AnotherClass为友元类
friend class AnotherClass;
};
class AnotherClass {
public:
void accessMyClassSecret(MyClass& obj) {
// 直接访问MyClass的私有成员
obj.secretValue = 100;
}
};
3. 内部类
在C++中,内部类(也称为嵌套类或嵌套类型)是在另一个类内部定义的类。内部类可以访问其外部类的私有和保护成员,但外部类不能访问内部类的私有成员。内部类主要用于组织和封装,使得某些功能仅与外部类相关联。
以下是内部类的一些特点和用法:
- 访问权限:内部类可以访问其外部类的所有成员,包括私有成员。
- 独立性:尽管内部类可以访问外部类的成员,但它是一个独立的类,可以有自己独立的成员和函数。
- 作用域:内部类的作用域仅限于其外部类,在外部类之外无法直接访问内部类。
- 实例化:要实例化内部类,必须先有一个外部类的实例。
class OuterClass {
private:
int outerValue;
public:
OuterClass() : outerValue(0) {}
class InnerClass { // 内部类的定义
private:
int innerValue;
public:
InnerClass() : innerValue(0) {}
void setInnerValue(int value) {
innerValue = value;
}
void setOuterValue(OuterClass& outer, int value) {
// 内部类可以访问外部类的私有成员
outer.outerValue = value;
}
};
// 外部类的方法可以返回内部类的实例
InnerClass getInnerClass() {
return InnerClass();
}
};
int main() {
OuterClass::InnerClass inner; // 错误:不能直接在外部类之外实例化内部类
OuterClass outer;
OuterClass::InnerClass inner = outer.getInnerClass(); // 正确:通过外部类的实例来创建内部类的实例
inner.setInnerValue(10);
inner.setOuterValue(outer, 20);
return 0;
}
4. 再次理解封装
封装是面向对象编程(OOP)的四大基本原则之一,与抽象、继承和多态并列。封装的核心思想是将对象的实现细节隐藏起来,只暴露出有限的接口与外界交互。这样做有以下几个目的:
1. 隐藏实现细节
封装允许我们将对象的内部状态和行为隐藏起来,只对外暴露必要的接口。这意味着对象的使用者不需要了解对象内部是如何工作的,只需要知道如何使用对象提供的接口。
2. 保护对象状态
通过封装,我们可以控制对对象内部成员的访问,防止外部直接访问和修改对象的状态,从而保护对象的一致性和完整性。通常,我们会将成员变量设置为私有(private),并通过公共(public)方法来访问和修改这些变量。
3. 提高代码的可维护性
封装使得代码更模块化,每个类都负责管理自己的状态和行为。当需要修改类的内部实现时,只要不改变公共接口,就不会影响到其他使用了该类的代码。这大大提高了代码的可维护性和可扩展性。
4. 降低耦合度
封装通过限制对象的直接交互,减少了对象间的依赖关系,降低了耦合度。低耦合的系统更易于理解和测试,也更容易重用和修改。
封装的具体实现
在C++中,封装通常通过以下方式实现:
-
访问修饰符:C++提供了三种访问修饰符:
public
、protected
和private
。public
成员可以被类的外部访问。protected
成员可以被类自身、子类以及友元函数访问。private
成员只能被类自身访问。
-
类:类是封装的基本单位,它定义了一个蓝图,用于创建具有特定属性和行为的对象。
-
构造函数和析构函数:用于初始化和清理对象的状态。
-
成员函数:提供对内部状态的访问和操作,通常声明为
public
。
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 <= balance) {
balance -= amount;
return true;
} else {
return false;
}
}
double getBalance() const { // 公共方法,用于获取余额
return balance;
}
};
int main() {
BankAccount account(1000.0);
account.deposit(500.0);
if (account.withdraw(200.0)) {
std::cout << "Withdrawal successful. Balance: " << account.getBalance() << std::endl;
} else {
std::cout << "Insufficient funds." << std::endl;
}
return 0;
}