这次主要讲类的基础、构造函数与析构函数的使用,以及继承和多态。
在C++中,类是一种用于创建对象的数据结构,它封装了数据(属性)和操作这些数据的方法(函数)。类定义了对象的特性和行为。
一个类通常包括以下部分:
public
、private
和 protected
,用于控制成员的访问权限。下面是一个简单的类定义和使用的例子:
#include
#include
// 定义一个类
class Person {
public: // 公有访问修饰符
// 构造函数
Person(std::string name, int age) : name(name), age(age) {}
// 成员函数
void introduce() {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
}
private: // 私有访问修饰符
// 数据成员
std::string name;
int age;
};
int main() {
// 创建Person类的对象
Person person("Alice", 30);
// 调用成员函数
person.introduce();
return 0;
}
在这个例子中:
Person
类有两个私有数据成员:name
和 age
。name
和 age
参数来初始化对象。introduce
是一个公共成员函数,用于打印对象的信息。main
函数中,我们创建了一个 Person
对象并调用了它的 introduce
方法。当然,让我们详细探讨C++中类的属性和方法,同时提供一个完整的代码示例。
public
:可以被任何外部代码访问。private
:只能被类的其他成员(方法或友元)访问。protected
:只能被类本身、派生类及友元访问。下面是一个类的属性和方法的简单示例:
#include
#include
class Car {
public:
// 构造函数
Car(std::string model, int year) : model(model), year(year) {}
// 公共方法
void displayInfo() {
std::cout << "Car Model: " << model << ", Year: " << year << std::endl;
}
// 设置年份
void setYear(int newYear) {
year = newYear;
}
// 获取年份
int getYear() {
return year;
}
private:
// 私有属性
std::string model;
int year;
};
int main() {
// 创建Car类的对象
Car myCar("Toyota", 2020);
// 调用公共方法
myCar.displayInfo();
// 更新年份
myCar.setYear(2022);
// 再次显示信息
myCar.displayInfo();
return 0;
}
在这个例子中:
Car
类有两个私有属性:model
和 year
。displayInfo
用于打印汽车信息。setYear
和 getYear
分别用于设置和获取年份。main
函数中,我们创建了一个 Car
对象,展示了如何使用这些方法来操作对象的状态。下面是一个包含构造函数和析构函数的C++类示例:
#include
#include
class Book {
public:
// 构造函数
Book(std::string title, std::string author) : title(title), author(author) {
std::cout << "Book '" << title << "' by " << author << " created." << std::endl;
}
// 析构函数
~Book() {
std::cout << "Book '" << title << "' by " << author << " destroyed." << std::endl;
}
// 显示书籍信息的方法
void displayInfo() {
std::cout << "Title: " << title << ", Author: " << author << std::endl;
}
private:
std::string title;
std::string author;
};
int main() {
// 创建Book对象
Book myBook("1984", "George Orwell");
// 使用对象的方法
myBook.displayInfo();
// 对象myBook在这里会被自动销毁,调用析构函数
return 0;
}
在这个例子中:
Book
类有两个私有属性:title
和 author
。main
函数中创建了一个 Book
对象,然后调用了它的 displayInfo
方法。程序结束时,myBook
对象的析构函数会被自动调用。构造函数和析构函数使得对象的初始化和销毁自动执行,有助于管理资源和避免内存泄漏。
在C++中,可以为一个类定义多个构造函数,只要它们的参数列表不同。这就是所谓的构造函数重载。重载构造函数允许以不同的方式初始化同一个类的对象。
下面的例子演示了如何在C++中重载构造函数:
#include
#include
class Rectangle {
public:
// 构造函数1:接受两个参数
Rectangle(int w, int h) : width(w), height(h) {
std::cout << "Rectangle created with width " << width << " and height " << height << std::endl;
}
// 构造函数2:接受一个参数
explicit Rectangle(int size) : width(size), height(size) {
std::cout << "Square created with size " << size << std::endl;
}
// 方法:计算面积
int getArea() const {
return width * height;
}
private:
int width, height;
};
int main() {
// 使用第一个构造函数
Rectangle rect1(10, 20);
// 使用第二个构造函数
Rectangle rect2(10);
std::cout << "Area of rect1: " << rect1.getArea() << std::endl;
std::cout << "Area of rect2: " << rect2.getArea() << std::endl;
return 0;
}
在这个例子中:
Rectangle
类有两个构造函数。第一个接受两个参数(宽和高),第二个仅接受一个参数(正方形的边长)。main
函数中,我们分别使用两种不同的构造函数创建了两个 Rectangle
对象。通过重载构造函数,Rectangle
类可以灵活地根据提供的参数来初始化对象,使得对象的创建更加灵活。
下面是一个展示拷贝构造函数和赋值运算符使用的示例:
#include
#include
class Person {
public:
// 构造函数
Person(std::string name) : name(name) {}
// 拷贝构造函数
Person(const Person& other) : name(other.name) {
std::cout << "Copied Person: " << name << std::endl;
}
// 赋值运算符
Person& operator=(const Person& other) {
if (this != &other) { // 防止自赋值
name = other.name;
}
return *this;
}
// 显示姓名
void printName() {
std::cout << "Person's name is: " << name << std::endl;
}
private:
std::string name;
};
int main() {
Person person1("Alice");
Person person2 = person1; // 调用拷贝构造函数
Person person3("Bob");
person3 = person1; // 调用赋值运算符
person1.printName();
person2.printName();
person3.printName();
return 0;
}
在这个示例中:
Person
类包含一个构造函数、一个拷贝构造函数和一个赋值运算符。person1
初始化 person2
时,调用了拷贝构造函数。person1
赋值给 person3
时,调用了赋值运算符。拷贝构造函数和赋值运算符对于管理资源(如动态分配的内存)非常重要,可以防止诸如浅拷贝和资源泄漏等问题。在设计类时,根据需要合理地实现这两个函数是非常重要的。
静态成员可以是变量或函数,它们属于类本身,而不是类的任何特定对象。这意味着静态成员由类的所有对象共享。
以下是一个包含静态成员的C++类的示例:
#include
class MyClass {
public:
// 构造函数
MyClass() {
// 每创建一个对象,计数器增加
objectCount++;
}
// 静态成员函数
static int getObjectCount() {
return objectCount;
}
private:
// 静态成员变量
static int objectCount;
};
// 初始化静态成员变量
int MyClass::objectCount = 0;
int main() {
MyClass obj1;
MyClass obj2;
MyClass obj3;
// 直接通过类名调用静态成员函数
std::cout << "Total objects: " << MyClass::getObjectCount() << std::endl;
return 0;
}
在这个例子中:
MyClass
类有一个静态成员变量 objectCount
,用于跟踪创建的对象数量。getObjectCount
,它返回 objectCount
的值。main
函数中,我们创建了三个 MyClass
的对象,并通过类名直接调用 getObjectCount
来显示创建的对象总数。继承是面向对象编程的一个基本概念,它允许基于一个类(基类)创建一个新的类(派生类)。这种机制提供了代码重用和层次化分类的能力。
#include
#include
// 基类
class Animal {
public:
Animal(std::string name) : name(name) {}
void eat() {
std::cout << name << " is eating." << std::endl;
}
protected:
std::string name;
};
// 派生类
class Dog : public Animal {
public:
Dog(std::string name) : Animal(name) {}
void bark() {
std::cout << name << " is barking." << std::endl;
}
};
int main() {
Dog myDog("Buddy");
myDog.eat(); // 调用基类方法
myDog.bark(); // 调用派生类方法
return 0;
}
在这个例子中:
Animal
是一个基类,具有一个公有方法 eat
和一个受保护的成员变量 name
。Dog
是从 Animal
公有继承而来的派生类。它继承了 Animal
的特性,并添加了自己的方法 bark
。main
函数中,我们创建了一个 Dog
对象,可以调用从基类继承的 eat
方法以及派生类的 bark
方法。当然,我可以提供私有继承和保护继承的示例,以进一步阐释这些继承类型在C++中的应用。
#include
#include
// 基类
class Vehicle {
public:
Vehicle(std::string type) : type(type) {}
protected:
std::string type;
};
// 派生类 - 私有继承
class Car : private Vehicle {
public:
Car(std::string type) : Vehicle(type) {}
void showType() {
std::cout << "Car type: " << type << std::endl; // 可以访问基类的保护成员
}
};
int main() {
Car myCar("SUV");
myCar.showType(); // 正确
// 下面的代码将会产生编译错误,因为type在Car中是私有的
// std::cout << myCar.type << std::endl;
return 0;
}
#include
#include
// 基类
class Device {
public:
Device(std::string name) : name(name) {}
protected:
std::string name;
};
// 派生类 - 保护继承
class Printer : protected Device {
public:
Printer(std::string name) : Device(name) {}
void printName() {
std::cout << "Printer name: " << name << std::endl; // 可以访问基类的保护成员
}
};
int main() {
Printer myPrinter("HP LaserJet");
myPrinter.printName(); // 正确
// 下面的代码将会产生编译错误,因为name在Printer中是保护的
// std::cout << myPrinter.name << std::endl;
return 0;
}
多态是面向对象编程的一个核心概念,允许对象以不同的方式响应相同的消息(或方法调用)。在C++中,多态主要通过虚函数(virtual functions)实现。
以下是一个展示C++中使用虚函数实现多态的示例:
#include
// 基类
class Shape {
public:
// 虚函数
virtual void draw() const {
std::cout << "Drawing a shape." << std::endl;
}
// 虚析构函数
virtual ~Shape() {}
};
// 派生类1
class Circle : public Shape {
public:
void draw() const override { // 重写虚函数
std::cout << "Drawing a circle." << std::endl;
}
};
// 派生类2
class Rectangle : public Shape {
public:
void draw() const override { // 重写虚函数
std::cout << "Drawing a rectangle." << std::endl;
}
};
void drawShape(const Shape& shape) {
shape.draw(); // 动态绑定
}
int main() {
Circle circle;
Rectangle rectangle;
drawShape(circle); // 输出: Drawing a circle.
drawShape(rectangle); // 输出: Drawing a rectangle.
return 0;
}
在这个示例中:
Shape
是一个基类,具有一个虚函数 draw
和一个虚析构函数。Circle
和 Rectangle
是从 Shape
派生的类,它们重写了 draw
方法。drawShape
函数接受一个 Shape
类型的引用,并调用 draw
方法。由于 draw
是虚函数,实际调用的是对象的动态类型对应的方法,这就是多态的体现。友元在C++中是一种特殊的机制,允许特定的函数或类访问另一个类的私有(private)和保护(protected)成员。
friend
关键字声明。以下是展示友元函数和友元类的示例:
#include
// 前向声明
class Box;
class Contents {
public:
explicit Contents(int value) : value(value) {}
// 友元函数声明
friend void showContents(const Box& b);
private:
int value;
};
class Box {
public:
explicit Box(int secret) : secret(secret) {}
// 友元类声明
friend class SecretInspector;
private:
int secret;
Contents contents{123}; // 假设内容是私有的
};
// 友元类
class SecretInspector {
public:
void inspect(const Box& b) {
std::cout << "Box secret: " << b.secret << std::endl;
}
};
// 友元函数实现
void showContents(const Box& b) {
std::cout << "Box contents: " << b.contents.value << std::endl;
}
int main() {
Box myBox(42);
SecretInspector inspector;
inspector.inspect(myBox); // 访问Box的私有数据
showContents(myBox); // 访问Box中Contents的私有数据
return 0;
}
在这个示例中:
Box
类有一个私有成员 secret
和一个私有嵌套对象 contents
。SecretInspector
类被声明为 Box
的友元类,因此它可以访问 Box
的所有私有成员。showContents
函数被声明为 Box
的友元函数,因此它可以访问 Box
中的 contents
的私有成员。运算符重载是一种形式的多态,允许你定义或改变运算符(如 +, -, *, / 等)在自定义类型(如类或结构体)上的行为。
operator
关键字后跟要重载的运算符符号。::
, .*
, .
和 ?:
)不能被重载。下面是一个如何重载加法运算符的示例:
#include
class Coordinate {
public:
Coordinate(int x, int y) : x(x), y(y) {}
// 运算符 '+' 重载
Coordinate operator+(const Coordinate& other) const {
return Coordinate(x + other.x, y + other.y);
}
// 输出坐标的友元函数
friend std::ostream& operator<<(std::ostream& os, const Coordinate& coord);
private:
int x, y;
};
// 输出运算符 '<<' 的重载
std::ostream& operator<<(std::ostream& os, const Coordinate& coord) {
os << "(" << coord.x << ", " << coord.y << ")";
return os;
}
int main() {
Coordinate point1(1, 2);
Coordinate point2(3, 4);
Coordinate sum = point1 + point2;
std::cout << "Sum of coordinates: " << sum << std::endl;
return 0;
}
在这个示例中:
Coordinate
类重载了加法运算符(+
),使其能够用于两个坐标对象的加法。+
返回一个新的 Coordinate
对象,其 x
和 y
值是两个操作数对象的 x
和 y
值之和。<<
)。模板类是一种强大的C++特性,允许编写与数据类型无关的通用代码。
template
定义模板,后面跟一个或多个模板参数。vector
、map
等)。以下是一个定义和使用模板类的示例:
#include
// 模板类定义
template<typename T>
class Box {
public:
Box(T content) : content(content) {}
T getContent() {
return content;
}
private:
T content;
};
int main() {
// 使用模板类创建整数盒子
Box<int> intBox(123);
// 使用模板类创建字符串盒子
Box<std::string> stringBox("Hello World");
std::cout << "Integer Box contains: " << intBox.getContent() << std::endl;
std::cout << "String Box contains: " << stringBox.getContent() << std::endl;
return 0;
}
在这个示例中:
Box
类是一个模板类,它有一个模板参数 T
。这个 T
可以在创建 Box
对象时被替换为任何类型。content
成员变量和 getContent
方法都使用了模板参数 T
。main
函数中,我们创建了两个 Box
对象:一个用于整数 (int
),另一个用于字符串 (std::string
)。virtual
关键字在继承时使用。= 0
表示)。#include
class Base {
public:
virtual void print() {
std::cout << "Base" << std::endl;
}
};
// 虚继承
class Derived1 : virtual public Base {
public:
void print() override {
std::cout << "Derived1" << std::endl;
}
};
class Derived2 : virtual public Base {
public:
void print() override {
std::cout << "Derived2" << std::endl;
}
};
class Final : public Derived1, public Derived2 {
public:
// 调用最近的覆盖
void print() override {
Derived2::print();
}
};
int main() {
Final obj;
obj.print(); // 输出 "Derived2"
// 通过基类引用访问
Base& baseRef = obj;
baseRef.print(); // 同样输出 "Derived2"
return 0;
}
在这个示例中:
Base
是一个基类,Derived1
和 Derived2
都通过虚继承继承自 Base
。Final
类从 Derived1
和 Derived2
继承,解决了潜在的菱形继承问题。print
方法在 Derived1
和 Derived2
中被覆盖,并在 Final
中再次覆盖。当然,我可以为多重继承、抽象类和接口提供相应的C++示例。
多重继承允许一个类同时继承自多个基类。
#include
// 基类1
class Printer {
public:
void print() {
std::cout << "Printing document" << std::endl;
}
};
// 基类2
class Scanner {
public:
void scan() {
std::cout << "Scanning document" << std::endl;
}
};
// 派生类,从两个基类继承
class MultifunctionMachine : public Printer, public Scanner {};
int main() {
MultifunctionMachine mfm;
mfm.print(); // 调用Printer的成员
mfm.scan(); // 调用Scanner的成员
return 0;
}
抽象类至少包含一个纯虚函数,并且不能直接实例化。
#include
// 抽象基类
class Shape {
public:
// 纯虚函数
virtual void draw() const = 0;
};
// 派生类
class Circle : public Shape {
public:
void draw() const override {
std::cout << "Drawing a circle." << std::endl;
}
};
int main() {
Circle circle;
circle.draw(); // 调用派生类的实现
// Shape shape; // 错误:不能实例化抽象类
return 0;
}
在C++中,接口可以通过完全抽象的类(只有纯虚函数)实现。
#include
// 接口
class Drawable {
public:
virtual void draw() const = 0;
};
// 实现接口的类
class Rectangle : public Drawable {
public:
void draw() const override {
std::cout << "Drawing a rectangle." << std::endl;
}
};
int main() {
Rectangle rect;
rect.draw(); // 调用具体实现
return 0;
}
这次从类的基础知识开始,讲了构造函数、析构函数、以及类的成员变量和方法。
接着,讲了重载构造函数、拷贝构造函数、赋值运算符以及静态成员。
然后讲了继承、多态、友元类和友元函数。
最后模板类的概念,还有一些高级特性的基本概念