1. 友元函数和友元类
- 什么是友元函数和友元类,它们的作用。
- 如何声明和使用友元函数和友元类,访问类的私有成员。
友元函数(Friend Functions)
友元函数是一种特殊的函数,它被允许访问类的私有成员。这意味着即使成员是私有的,友元函数也能够直接访问它们,而不需要通过公有接口。这提供了更多的灵活性,允许外部函数与类密切合作。
示例-演示如何声明和使用友元函数:
#include <iostream>
class MyClass {
private:
int secretData;
public:
MyClass() : secretData(0) {}
friend void FriendFunction(MyClass& obj); // 友元函数的声明
};
// 友元函数的定义
void FriendFunction(MyClass& obj) {
obj.secretData = 42; // 可以访问私有成员
}
int main() {
MyClass myObj;
FriendFunction(myObj); // 调用友元函数
std::cout << myObj.secretData << std::endl; // 输出 42
return 0;
}
示例中,FriendFunction
被声明为 MyClass
的友元函数,可以直接访问 secretData
私有成员。
友元类(Friend Classes)
友元类与友元函数类似,但它允许整个类成为另一个类的友元,而不仅仅是一个函数。这意味着友元类的所有成员都可以访问其他类的私有成员。
class MyClass {
private:
int secretData;
public:
MyClass() : secretData(0) {}
friend class FriendClass; // 友元类的声明
};
class FriendClass {
public:
void AccessPrivateData(MyClass& obj) {
obj.secretData = 42; // 可以访问私有成员
}
};
int main() {
MyClass myObj;
FriendClass friendObj;
friendObj.AccessPrivateData(myObj); // 通过友元类访问私有成员
std::cout << myObj.secretData << std::endl; // 输出 42
return 0;
}
FriendClass
被声明为 MyClass
的友元类,因此它的成员函数可以访问 secretData
私有成员。
2. 拷贝构造函数
- 介绍拷贝构造函数的概念。
- 定义和使用拷贝构造函数,以处理对象的复制。
拷贝构造函数是C++中的一个特殊构造函数,用于创建一个对象的副本。当对象按值传递给函数、作为函数的返回值返回或者在初始化过程中需要复制时,拷贝构造函数会被自动调用。它用于确保对象的复制是正确的,包括成员变量的深拷贝。
示例-演示了如何定义和使用拷贝构造函数:
#include <iostream>
#include <cstring>
class MyString {
private:
char* str;
public:
// 构造函数,用于创建字符串对象
MyString(const char* s) {
str = new char[strlen(s) + 1];
strcpy(str, s);
}
// 拷贝构造函数,用于创建对象的副本
MyString(const MyString& other) {
str = new char[strlen(other.str) + 1];
strcpy(str, other.str);
}
// 析构函数,用于释放内存
~MyString() {
delete[] str;
}
// 显示字符串内容
void display() {
std::cout << str << std::endl;
}
};
int main() {
MyString original("Hello, World!");
MyString copy = original; // 使用拷贝构造函数创建副本
original.display(); // 输出 "Hello, World!"
copy.display(); // 输出 "Hello, World!"
return 0;
}
在示例中,首先定义了一个 MyString
类,它包含一个字符数组 str
用于存储字符串。然后,定义一个拷贝构造函数,通过分配新内存并复制原始对象的内容来创建副本。最后,在 main
函数中,创建了一个 original
对象,并使用拷贝构造函数创建了 copy
对象。这两个对象分别存储相同的字符串内容,但它们在内存中有不同的副本。
3. 运算符重载
- 解释运算符重载的概念。
- 提供示例,说明如何重载常见的运算符,如+、-、*等。
运算符重载是C++中一种强大的特性,它允许为自定义类创建特定的运算符行为。通过运算符重载,可以让对象像内置类型一样执行加法、减法、乘法等操作,使代码更直观和易读。
示例-演示如何重载加法运算符:
#include <iostream>
class Complex {
private:
double real;
double imag;
public:
Complex(double r, double i) : real(r), imag(i) {}
// 运算符重载:重载+运算符,实现复数相加
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
// 显示复数
void display() const {
std::cout << real << " + " << imag << "i" << std::endl;
}
};
int main() {
Complex num1(3.0, 2.0);
Complex num2(1.5, 4.5);
Complex result = num1 + num2; // 使用重载的+运算符
num1.display(); // 输出 "3 + 2i"
num2.display(); // 输出 "1.5 + 4.5i"
result.display(); // 输出 "4.5 + 6.5i"
return 0;
}
在示例中,定义一个 Complex
类表示复数。然后,重载加法运算符 +
,使得两个 Complex
对象可以像内置数值类型一样相加。通过运算符重载,让复数对象的操作更自然和直观。
4. 静态成员和静态函数
- 讲解静态成员和静态函数的作用。
- 如何声明和使用静态成员和静态函数。
在C++中,静态成员和静态函数是属于整个类而不是类的实例的。它们被称为类级别的成员,与类的每个实例无关,而是与类本身关联。
静态成员是在类级别共享的数据成员。它们对于所有类的实例都是相同的。要声明静态成员,可以使用 static
关键字。
#include <iostream>
class MyClass {
public:
static int count; // 静态成员变量
MyClass() {
count++; // 每次创建实例时增加计数
}
static void showCount() {
std::cout << "Total instances: " << count << std::endl;
}
};
int MyClass::count = 0; // 初始化静态成员变量
int main() {
MyClass obj1;
MyClass obj2;
MyClass obj3;
MyClass::showCount(); // 调用静态函数显示计数
return 0;
}
我们创建一个名为 MyClass
的类,包含一个静态整数 count
用于跟踪创建的实例数。创建 MyClass
的实例时,静态成员变量 count
都会增加。定义一个静态函数 showCount
来显示实例的总数。
静态函数是在类级别共享的成员函数。它们不需要访问类的实例数据,因此可以在没有实例的情况下调用。静态函数使用与类相关的方式调用,而不是使用实例。
5. 类的继承和多态性
- 介绍类的继承的概念,包括基类和派生类。
- 讲解多态性的概念和实现方式,包括虚函数和运行时多态性。
在C++中,类的继承和多态性是面向对象编程的核心概念之一。它们允许构建更强大、更灵活的对象模型。
类的继承允许创建一个新的类(称为派生类),它可以继承另一个类(称为基类)的属性和行为。可以在现有类的基础上创建新类,而不必从头开始编写代码。派生类可以添加额外的成员变量和成员函数,也可以覆盖基类的成员函数以改变其行为。
#include <iostream>
// 基类
class Shape {
public:
virtual void draw() {
std::cout << "绘制形状" << std::endl;
}
};
// 派生类
class Circle : public Shape {
public:
void draw() override {
std::cout << "绘制圆形" << std::endl;
}
};
int main() {
Shape shape;
Circle circle;
shape.draw(); // 输出:绘制形状
circle.draw(); // 输出:绘制圆形
return 0;
}
基类 Shape
和一个派生类 Circle
。派生类继承了基类的 draw
函数,并覆盖了它以提供不同的行为。在 main
函数中,我们创建了基类和派生类的对象,然后调用它们的 draw
函数,演示了多态性的概念。
多态性是一种能够在运行时选择正确函数版本的机制。在上面的示例中,Shape
类的 draw
函数是虚函数,而 Circle
类中的 draw
函数使用了 override
关键字来表示它是一个覆盖了基类函数的虚函数。这允许我们在基类指针或引用的上下文中调用派生类的函数,而选择的是正确的版本。
6. 示例和练习
- 示例代码,演示友元函数、拷贝构造函数、运算符重载、静态成员、类的继承和多态性的用法。
- 练习,以加强对这些高级主题的理解和应用。
1. 创建一个类 Fraction
表示分数,包括分子和分母。编写运算符重载函数,实现分数的加法和减法运算:
#include <iostream>
class Fraction {
private:
int numerator;
int denominator;
public:
Fraction(int num, int den) : numerator(num), denominator(den) {
// Ensure denominator is not zero
if (denominator == 0) {
std::cerr << "Error: Denominator cannot be zero." << std::endl;
exit(1);
}
}
// Overload the + operator to add fractions
Fraction operator+(const Fraction& other) const {
int newNumerator = numerator * other.denominator + other.numerator * denominator;
int newDenominator = denominator * other.denominator;
return Fraction(newNumerator, newDenominator);
}
// Overload the - operator to subtract fractions
Fraction operator-(const Fraction& other) const {
int newNumerator = numerator * other.denominator - other.numerator * denominator;
int newDenominator = denominator * other.denominator;
return Fraction(newNumerator, newDenominator);
}
void display() const {
std::cout << numerator << "/" << denominator << std::endl;
}
};
int main() {
Fraction frac1(1, 2);
Fraction frac2(1, 3);
Fraction sum = frac1 + frac2;
Fraction diff = frac1 - frac2;
std::cout << "Fraction 1: ";
frac1.display();
std::cout << "Fraction 2: ";
frac2.display();
std::cout << "Sum: ";
sum.display();
std::cout << "Difference: ";
diff.display();
return 0;
}
解答说明:
- 创建名为
Fraction
的类,表示分数,包括分子和分母属性。 - 通过运算符重载,重载
+
和-
运算符,实现了分数的加法和减法。 - 在
main()
函数中,创建两个分数对象frac1
和frac2
,然后对它们进行加法和减法运算。
2. 创建一个基类 Vehicle
表示交通工具,包括名称和速度属性。创建两个派生类 Car
和 Bike
,它们继承了基类并添加了特定的属性:
#include <iostream>
#include <string>
class Vehicle {
protected:
std::string name;
double speed;
public:
Vehicle(const std::string& n, double s) : name(n), speed(s) {}
void display() const {
std::cout << "Name: " << name << ", Speed: " << speed << " km/h" << std::endl;
}
};
class Car : public Vehicle {
private:
int numWheels;
public:
Car(const std::string& n, double s, int wheels) : Vehicle(n, s), numWheels(wheels) {}
void display() const {
std::cout << "Car - ";
Vehicle::display();
std::cout << "Wheels: " << numWheels << std::endl;
}
};
class Bike : public Vehicle {
private:
bool hasBasket;
public:
Bike(const std::string& n, double s, bool basket) : Vehicle(n, s), hasBasket(basket) {}
void display() const {
std::cout << "Bike - ";
Vehicle::display();
std::cout << "Basket: " << (hasBasket ? "Yes" : "No") << std::endl;
}
};
int main() {
Car car("Sedan", 120.0, 4);
Bike bike("Mountain Bike", 25.0, true);
car.display();
bike.display();
return 0;
}
解答说明:
- 创建一个基类
Vehicle
,包括名称和速度属性。 - 然后,创建两个派生类
Car
和Bike
,它们继承了基类Vehicle
并添加了特定的属性。 - 在
main()
函数中,创建了一个Car
对象和一个Bike
对象,分别调用它们的display()
方法以显示车辆信息,包括名称、速度等。
3. 使用多态性存储和调用不同类型的动物对象:
#include <iostream>
#include <vector>
class Animal {
public:
virtual void speak() const {
std::cout << "Animal speaks." << std::endl;
}
};
class Dog : public Animal {
public:
void speak() const override {
std::cout << "Dog barks." << std::endl;
}
};
class Cat : public Animal {
public:
void speak() const override {
std::cout << "Cat meows." << std::endl;
}
};
int main() {
std::vector<Animal*> animals;
animals.push_back(new Dog());
animals.push_back(new Cat());
for (const auto& animal : animals) {
animal->speak();
delete animal; // Don't forget to free memory
}
return 0;
}
解答说明:
- 定义一个基类
Animal
,创建两个派生类Dog
和Cat
。 - 在派生类中重写了虚函数
speak()
以实现不同类型的动物的声音。 - 使用指向基类的指针存储不同类型的动物对象,实现了多态性。
- 在循环中,通过基类指针调用虚函数
speak()
,实际执行的是派生类的版本。
4. 银行账户类 BankAccount
和友元函数 transfer
:
#include <iostream>
class BankAccount; // Forward declaration
class BankAccount {
private:
std::string accountNumber;
double balance;
public:
BankAccount(const std::string& accNum, double initBalance) : accountNumber(accNum), balance(initBalance) {}
void displayBalance() const {
std::cout << "Account: " << accountNumber << ", Balance: " << balance << " USD" << std::endl;
}
friend void transfer(BankAccount& from, BankAccount& to, double amount);
};
void transfer(BankAccount& from, BankAccount& to, double amount) {
if (from.balance >= amount) {
from.balance -= amount;
to.balance += amount;
std::cout << "Transfer successful." << std::endl;
} else {
std::cout << "Insufficient balance for transfer." << std::endl;
}
}
int main() {
BankAccount acc1("12345", 1000.0);
BankAccount acc2("67890", 500.0);
acc1.displayBalance();
acc2.displayBalance();
transfer(acc1, acc2, 300.0); // Transfer money from acc1 to acc2
acc1.displayBalance();
acc2.displayBalance();
return 0;
}
解答说明:
- 定义一个银行账户类
BankAccount
,包括账户号码和余额属性。 - 使用友元函数
transfer
实现了账户之间的资金转移。 - 在
main()
中,创建两个账户,显示它们的余额,然后进行转账操作。
5. 图形类 Shape
和派生类 Circle
和 Rectangle
,计算总面积:
#include <iostream>
#include <vector>
class Shape {
protected:
std::string color;
public:
Shape(const std::string& c) : color(c) {}
virtual double getArea() const {
return 0.0; // Default area for a generic shape
}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(const std::string& c, double r) : Shape(c), radius(r) {}
double getArea() const override {
return 3.14159 * radius * radius; // Area of a circle
}
};
class Rectangle : public Shape {
private:
double width;
double height;
public:
Rectangle(const std::string& c, double w, double h) : Shape(c), width(w), height(h) {}
double getArea() const override {
return width * height; // Area of a rectangle
}
};
int main() {
std::vector<Shape*> shapes;
shapes.push_back(new Circle("Red", 5.0));
shapes.push_back(new Rectangle("Blue", 4.0, 6.0));
double totalArea = 0.0;
for (const auto& shape : shapes) {
totalArea += shape->getArea();
delete shape; // Don't forget to free memory
}
std::cout << "Total Area: " << totalArea << std::endl;
return 0;
}
解答说明:
- 定义一个基类
Shape
,创建两个派生类Circle
和Rectangle
,重写虚函数getArea()
以返回不同形状的面积。 - 使用指向基类的指针存储不同类型的图形对象,实现了多态性。
- 在循环中,计算了所有图形的总面积。