(一)友元
友元在 C++ 中是一种特殊的存在,其主要作用是打破类的访问控制权限。类中的成员通过 public、private 和 protected 三种访问修饰符设置访问权限,private 和 protected 成员对类外部代码而言通常不可直接访问。但在某些特定场景下,我们期望特定函数或类能够访问这些受限成员,此时友元便发挥作用。
友元可以是函数,也可以是类。当一个函数被声明为某个类的友元函数时,该函数便能访问该类的所有成员,包括 private 和 protected 成员。例如,我们创建一个Circle类来表示圆,radius设为 private 成员:
class Circle
{
private:
double radius;
public:
Circle(double r) : radius(r) {}
// 声明友元函数
friend double calculateArea(Circle c);
};
// 友元函数的定义
double calculateArea(Circle c)
{
return 3.14 * c.radius * c.radius;
}
在上述代码中, calculateArea函数被声明为Circle类的友元函数,因此它能够直接访问Circle类的 private 成员radius来计算圆的面积。
除了友元函数,还有友元类。当一个类 A 被声明为另一个类 B 的友元类时,类 A 的所有成员函数都可以访问类 B 的所有成员。这在一些复杂的类关系中极为有用,比如一个 “管理类” 需要对多个 “被管理类” 进行深度操作时。
class B
{
private:
int privateData;
public:
B(int data) : privateData(data) {}
// 声明A为友元类
friend class A;
};
class A
{
public:
void accessBData(B& b)
{
// 可以直接访问B的private成员
cout << "Accessed private data of B: " << b.privateData << endl;
}
};
使用友元虽然便捷,但不可滥用。因为它破坏了类的封装性,过多使用可能导致代码可维护性下降。所以在使用友元时,要考量是否确有必要打破访问限制。
1.友元与运算符重载
友元在运算符重载中也有重要应用。以复数类Complex为例,我们希望重载+运算符实现复数相加:
class Complex
{
private:
double real;
double imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// 声明友元函数用于运算符重载
friend Complex operator+(const Complex& c1, const Complex& c2);
};
Complex operator+(const Complex& c1, const Complex& c2)
{
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
通过将operator函数声明为Complex类的友元,我们可以直接访问类的私有成员,从而实现更自然的复数相加操作。(类的友元函数并不包含this指针,普通成员函数属于类的一部分,依赖于特定的对象实例来调用,所以需要this指针来明确操作的是哪个对象的数据。而友元函数不是类的成员函数,它只是被授予了访问类中私有和保护成员的特殊权限。)
2.友元与模板类
在模板类中,友元同样能发挥独特作用。假设有一个通用的Static模板类,我们可能希望特定的模板实例化版本或其他类能够访问其内部成员:
template <typename T>
class Stack
{
private:
T* data;
int top;
public:
Stack() : data(nullptr), top(-1) {}
// 声明友元类
template <typename U>
friend class StackDebugger;
};
template <typename U>
class StackDebugger
{
public:
static void printStackInfo(Stack<U>& s)
{
// 访问Stack类的私有成员进行调试输出
cout << "Stack top: " << s.top << endl;
}
};
这样,StackDebugger类的printStackInfo函数就Stack类的私有成员,方便进行调试和监控。
(二)static
在 C++ 中,static 关键字用途广泛,这里主要聚焦其在类中的应用,即静态成员和静态成员函数。
1.静态成员
静态成员变量属于类,而非某个具体对象。这意味着无论创建多少个类的对象,静态成员变量仅存在一份实例,所有对象共享该变量。例如,我们创建一个Student类,希望统计创建的学生对象数量,此时可使用静态成员变量:
class Student
{
private:
static int studentCount;
string name;
public:
Student(string n) : name(n)
{
studentCount++;
}
~Student()
{
studentCount--;
}
static int getStudentCount()
{
return studentCount;
}
};
// 初始化静态成员变量
int Student::studentCount = 0;
在上述代码中,studentCount是静态成员变量,每次创建对象时,其值增加;每次销毁对象时,其值减少。通过静态成员函数getStudentCount,我们可获取当前学生数量。
2.静态成员函数
静态成员函数同样属于类,而非某个对象。它只能访问静态成员变量和其他静态成员函数,无法访问非静态成员,因为非静态成员依赖于具体对象存在。就像上述的getStudentCount函数,作为静态成员函数,它只能访问静态成员变量studentCount。
(static函数同样不包含this指针,静态函数的主要目的是提供与类相关但不依赖于对象实例的功能。它们通常用于执行不依赖于对象状态的操作,例如执行通用的计算或访问静态成员变量。由于它们不依赖于任何对象的状态,所以不需要this指针)。
int main()
{
Student s1("张三");
Student s2("李四");
cout << "Total students: " << Student::getStudentCount() << endl;
return 0;
}
静态成员函数和变量在许多场景中都非常实用,比如在实现单例模式时,就会用到静态成员来确保全局仅有一个类的实例。
3.static 与多态
虽然静态成员函数不能参与多态,但静态成员变量可以在多态环境下发挥独特作用。例如,在一个继承体系中,基类和派生类共享同一个静态成员变量,通过这个变量可以实现一些跨类的统计或状态管理功能:
class Animal
{
public:
static int animalCount;
Animal()
{
animalCount++;
}
virtual ~Animal()
{
animalCount--;
}
virtual void makeSound() = 0;
};
int Animal::animalCount = 0;
class Dog : public Animal
{
public:
void makeSound() override
{
cout << "Woof!" << endl;
}
};
class Cat : public Animal
{
public:
void makeSound() override
{
cout << "Meow!" << endl;
}
};
在这个例子中,animalcount统计了所有动物及其派生类对象的总数,无论对象是谁,都共享这个静态变量。
4.static 与命名空间
在命名空间中,static 关键字也有特殊含义。在文件作用域内使用 static 修饰变量或函数,这些变量和函数的作用域将被限制在当前文件中,对外不可见。这有助于避免不同文件之间的命名冲突,提高代码的模块化程度。例如:
// file1.cpp
static int filePrivateVar = 0;
static void filePrivateFunc()
{
// 函数实现
}
// file2.cpp
// 这里无法访问file1.cpp中的filePrivateVar和filePrivateFunc
572

被折叠的 条评论
为什么被折叠?



