高级技术与实用工具
在掌握了C++的核心语言特性之后,我们需要进一步了解一些高级技术和实用工具。这些工具虽然在日常编程中使用频率相对较低,但在大型项目开发和特定应用场景中却发挥着重要作用。它们能够帮助我们编写更加稳定、可维护和高效的代码。
在这最后一节,我们将介绍几个重要的高级主题:异常处理机制让我们能够优雅地处理程序运行时的错误情况;命名空间提供了组织大型程序的有效手段;
多重继承和虚继承扩展了面向对象编程的能力;枚举类型为我们提供了类型安全的常量管理方式;类成员指针则让我们能够以更灵活的方式操作类的成员。
异常处理
在程序运行过程中,各种意外情况都可能发生:文件无法打开、内存分配失败、网络连接中断等等。传统的错误处理方式往往会导致代码逻辑混乱,而异常处理机制为我们提供了一种更加优雅和系统化的错误管理方式。
异常处理的核心思想是将错误检测和错误处理分离。程序的一部分负责检测问题并抛出异常,而另一部分则负责捕获和处理这些异常。这种分离使得代码结构更加清晰,也让错误处理逻辑更加集中和易于维护。
抛出异常的机制
当程序遇到无法正常处理的情况时,可以通过throw语句抛出一个异常。异常本质上是一个对象,它携带了关于错误情况的信息。让我们通过一个文件处理的例子来说明异常的抛出:
#include <fstream>
#include <stdexcept>
#include <string>
class FileProcessor {
public:
void processFile ( const std :: string & filename ) {
std ::ifstream file (filename);
if ( ! file. is_open ()) {
throw std :: runtime_error ( "无法打开文件:" + filename);
}
// 处理文件内容
std ::string line;
while ( std :: getline (file, line)) {
if (line. empty ()) {
throw std :: invalid_argument ( "文件包含空行,无法处理" );
}
// 处理每一行的逻辑
}
}
};
当throw语句执行时,程序的正常执行流程被中断,控制权转移到异常处理机制。这个过程被称为栈展开(stack unwinding)。在栈展开过程中,程序会依次退出当前函数的作用域,寻找能够处理该异常的catch语句。
捕获和处理异常
异常的捕获通过try-catch语句块来实现。try块包含可能抛出异常的代码,而catch块则负责处理特定类型的异常:
#include <iostream>
#include <exception>
void demonstrateExceptionHandling () {
FileProcessor processor;
try {
processor. processFile ( "data.txt" );
std ::cout << "文件处理成功完成" << std ::endl;
}
catch ( const std ::runtime_error & e) {
std ::cerr << "运行时错误:" << e. what
catch语句的匹配遵循严格的规则。异常对象的类型必须与catch参数的类型完全匹配,只有少数几种类型转换被允许:从非const到const的转换、派生类到基类的转换,以及数组到指针和函数到函数指针的转换。
构造函数中的异常处理
构造函数的异常处理需要特别注意,因为构造函数的初始化列表在函数体执行之前就开始执行。如果初始化列表中抛出异常,构造函数体内的catch语句无法捕获它。为了处理这种情况,C++提供了函数try块的语法:
class DatabaseConnection {
private:
std ::string connectionString;
std ::unique_ptr < Connection > conn;
public:
DatabaseConnection ( const std :: string & host , int port )
try : connectionString (host + ":" + std :: to_string (port)),
conn ( std ::
noexcept规范:承诺不抛出异常
在某些情况下,我们可以确信某个函数不会抛出异常。这时可以使用noexcept规范来告诉编译器和用户这个保证。这不仅有助于代码的可读性,还能让编译器进行更好的优化:
class SafeCalculator {
public:
// 基本算术运算通常不会抛出异常
int add ( int a , int b ) noexcept {
return a + b;
}
int multiply ( int a , int b ) noexcept {
return a * b;
}
需要注意的是,如果一个标记为noexcept的函数实际上抛出了异常,程序会调用std::terminate终止执行。因此,只有在完全确信函数不会抛出异常时,才应该使用noexcept规范。
异常类的层次结构
C++标准库提供了一套完整的异常类层次结构,所有标准异常都继承自std::exception基类。在实际应用中,我们通常会扩展这个层次结构来定义特定于应用领域的异常类型:
// 自定义异常基类
class GameException : public std :: exception {
protected:
std ::string message;
public:
explicit GameException ( const std :: string & msg ) : message (msg) {}
virtual const char* what () const noexcept override {
return message. c_str ();
命名空间
随着程序规模的增长,命名冲突成为了一个日益严重的问题。不同的库可能定义了相同名称的函数、类或变量,这会导致编译错误或意外的行为。命名空间为我们提供了一种有效的解决方案,它将程序的不同部分隔离到不同的作用域中。
命名空间的定义和使用
命名空间本质上是一种作用域机制。我们可以将相关的声明和定义放在同一个命名空间中,从而避免与其他部分的名称冲突:
// 图形库的命名空间
namespace Graphics {
class Point {
private:
double x, y;
public:
Point ( double x = 0 , double y = 0 ) : x (x), y (y) {}
double getX () const { return x; }
double getY ()
命名空间的一个重要特性是它们可以是不连续的。我们可以在多个地方为同一个命名空间添加成员,这使得我们能够将接口和实现分离到不同的文件中:
// Graphics.h - 接口声明
namespace Graphics {
class Renderer {
public:
void renderScene ();
void setViewport ( int width , int height );
private:
int viewportWidth, viewportHeight;
};
void initializeGraphicsSystem ();
}
// Graphics.cpp - 实现定义
namespace Graphics {
void Renderer ::
using声明和using指令
为了方便使用命名空间中的成员,C++提供了using声明和using指令。using声明允许我们将特定的名称引入当前作用域,而using指令则将整个命名空间的所有名称都引入:
void demonstrateUsing () {
// 使用完全限定名称
Graphics ::Point p1 ( 1.0 , 2.0 );
Audio ::Point p2 ( 440.0 , 0.8 );
// using声明:引入特定名称
using Graphics ::Point;
using Graphics ::Circle;
Point p3 ( 3.0 , 4.0 ); // 现在Point指的是Graphics::Point
Circle c
命名空间与重载
命名空间对函数重载有着重要影响。当我们调用一个函数时,编译器不仅会在当前作用域中查找,还会在参数类型所属的命名空间中查找。这种机制被称为参数依赖查找(ADL)或Koenig查找:
namespace MathUtils {
class Vector3D {
private:
double x, y, z;
public:
Vector3D ( double x , double y , double z ) : x (x), y (y), z (z) {}
double getX () const { return x; }
double getY () const
多重继承
虽然单一继承已经能够满足大多数编程需求,但在某些情况下,一个类需要同时继承多个基类的特性。多重继承为我们提供了这种能力,但同时也带来了一些复杂性和需要注意的问题。
多重继承的基本语法
多重继承允许一个类同时从多个基类派生。在派生列表中,我们可以指定多个基类,每个基类都有自己的访问说明符:
// 基类:交通工具
class Vehicle {
protected:
std ::string brand;
int year;
public:
Vehicle ( const std :: string & brand , int year ) : brand (brand), year (year) {}
virtual void start () = 0 ;
virtual void stop ()
在多重继承中,派生类的对象包含来自所有基类的子对象。构造函数的调用顺序遵循基类在派生列表中出现的顺序,而不是构造函数初始化列表中的顺序。
类型转换和歧义解决
多重继承的一个重要特点是派生类对象可以转换为任何一个基类的指针或引用。但这也可能导致歧义问题:
void demonstrateConversions () {
ElectricCar tesla ( "Tesla" , 2023 , 500.0 );
// 可以转换为任何基类的指针
Vehicle * vehicle = & tesla;
ElectronicDevice * device = & tesla;
// 调用对应的方法
vehicle-> start (); // 调用ElectricCar::start()
device-> turnOn (); // 调用ElectricCar::turnOn()
// 如果两个基类有同名方法,可能出现歧义
虚继承:解决钻石问题
当继承层次结构形成钻石形状时(即一个类通过多条路径继承自同一个基类),会出现钻石问题。虚继承是解决这个问题的标准方法:
// 公共基类
class Animal {
protected:
std ::string name;
int age;
public:
Animal ( const std :: string & name , int age ) : name (name), age (age) {}
virtual void makeSound () = 0 ;
virtual void move () =
枚举类型
枚举类型为我们提供了一种定义命名常量的方式。C++11引入了作用域枚举(scoped enumeration),解决了传统枚举类型的一些问题,提供了更好的类型安全性。
作用域枚举与传统枚举
作用域枚举使用enum class关键字定义,它的枚举值不会污染外围作用域:
// 传统枚举
enum Color { RED , GREEN , BLUE };
// 作用域枚举
enum class TrafficLight { RED , YELLOW , GREEN };
enum class Status { ACTIVE , INACTIVE , PENDING };
void demonstrateEnums () {
// 传统枚举的枚举值直接在外围作用域中
Color c1 = RED; // 正确
指定底层类型和自定义值
我们可以为枚举指定底层类型,并为枚举值设定自定义的数值:
// 指定底层类型为char,节省内存
enum class Priority : char {
LOW = 1 ,
MEDIUM = 5 ,
HIGH = 10 ,
CRITICAL = 20
};
// 错误代码枚举
enum class ErrorCode : int {
SUCCESS = 0 ,
FILE_NOT_FOUND = 404 ,
ACCESS_DENIED
枚举的实际应用
枚举类型特别适用于状态机、配置选项和错误处理等场景:
class FileManager {
public:
enum class OpenMode {
READ_ONLY ,
WRITE_ONLY ,
READ_WRITE ,
APPEND
};
enum class FileType {
TEXT ,
BINARY ,
COMPRESSED
};
private:
std ::string filename;
OpenMode mode;
FileType type;
bool
类成员指针
类成员指针是C++中一个相对高级但非常有用的特性。它允许我们在运行时动态地选择要访问的类成员,从而实现更加灵活的设计。
数据成员指针
数据成员指针指向类的某个数据成员,但不指向特定的对象。要使用数据成员指针,我们需要提供具体的对象:
class Student {
private:
std ::string name;
int age;
double gpa;
public:
Student ( const std :: string & name , int age , double gpa )
: name (name), age (age), gpa (gpa) {}
// 为了演示,提供一些访问器
const std ::
成员函数指针
成员函数指针的语法更加复杂,但功能也更强大。我们可以存储指向成员函数的指针,并在运行时决定调用哪个函数:
class Calculator {
public:
double add ( double a , double b ) const { return a + b; }
double subtract ( double a , double b ) const { return a - b; }
double multiply ( double a , double b )
成员指针的高级应用
成员指针在实现反射机制、属性系统和动态方法调用时特别有用:
class PropertySystem {
public:
template < typename T , typename MemberType >
class Property {
private:
MemberType T :: * memberPtr;
public:
Property ( MemberType T :: * ptr ) : memberPtr (ptr) {}
void set ( T &
联合体
联合体允许在同一内存位置存储不同类型的数据,但同一时间只能存储其中一种类型的值。现代C++中的联合体具有更强的功能,可以包含具有构造函数的类类型成员。
传统联合体的应用
联合体最常见的用途是实现变体类型,即能够存储多种不同类型值的数据结构:
// 消息系统中的不同消息类型
enum class MessageType {
TEXT ,
NUMBER ,
BOOLEAN ,
COORDINATE
};
struct TextMessage {
char content[ 256 ];
};
struct NumberMessage {
double value;
};
struct BoolMessage {
bool flag;
};
struct CoordinateMessage
类似联合体的类
对于包含需要构造和析构的类类型成员的联合体,我们需要显式管理对象的生命周期:
class VariantValue {
public:
enum class Type { NONE , STRING , VECTOR , MAP };
private:
Type type;
union {
std ::string strValue;
std ::vector <int> vecValue;
std ::map < std ::string, int> mapValue;
};
public:
局部类和嵌套类
在某些特殊情况下,我们可能需要在函数内部或另一个类内部定义类。C++支持局部类(在函数内定义的类)和嵌套类(在类内定义的类),它们为特定的设计需求提供了解决方案。
局部类的应用
局部类定义在函数体内,它的作用域仅限于定义它的函数。局部类通常用于实现函数内部的辅助功能:
#include <vector>
#include <algorithm>
#include <functional>
std :: vector < int > processData ( const std :: vector < int > & input ) {
// 局部类:定义特定的比较策略
class CustomComparator {
private:
bool reverse;
public:
CustomComparator ( bool rev =
嵌套类的设计模式
嵌套类是定义在另一个类内部的类,它可以访问外围类的所有成员,包括私有成员。嵌套类常用于实现迭代器、辅助类或与外围类紧密相关的功能:
class TreeNode {
public:
// 嵌套类:树的迭代器
class Iterator {
private:
TreeNode * current;
std ::stack < TreeNode *> nodeStack;
public:
Iterator ( TreeNode * root ) : current ( nullptr ) {
if (root) {
pushLeftBranch (root);
advance ();
}
练习
现在让我们通过一些简单的练习题来巩固本章学到的知识。每道题都涵盖了C++高级特性的核心概念,请先尝试独立完成,然后再查看答案。
练习 1:异常处理的基本使用
编写一个函数,读取用户输入的两个整数并计算它们的商。如果除数为0,抛出异常并处理。
#include <iostream>
#include <stdexcept>
using namespace std ;
double divide ( int a , int b ) {
if (b == 0 ) {
throw runtime_error ( "除数不能为0!" );
}
return static_cast<double> (a) / b;
}
int main () {
int
练习 2:命名空间的使用
定义两个命名空间MathLib和StringLib,分别包含add函数,演示如何避免命名冲突。
#include <iostream>
#include <string>
using namespace std ;
namespace MathLib {
double add ( double a , double b ) {
return a + b;
}
double multiply ( double a , double b ) {
return a * b;
练习 3:作用域枚举的使用
使用enum class设计一个简单的状态机,表示设备的状态(空闲、运行、停止)。
#include <iostream>
#include <string>
using namespace std ;
enum class DeviceState {
IDLE , // 空闲
RUNNING , // 运行
STOPPED // 停止
};
class Device {
private:
DeviceState state_;
string name_;
public:
Device ( const string & name ) :
练习 4:简单的多重继承
实现一个MultiFunctionDevice类,同时继承Printer和Scanner接口。
#include <iostream>
#include <string>
using namespace std ;
class Printer {
public:
virtual void print ( const string & document ) = 0 ;
virtual ~Printer () = default ;
};
class Scanner {
public:
virtual string scan
练习 5:综合应用——异常安全的资源管理
实现一个简单的资源管理类,在构造函数中分配资源,析构函数中释放资源,并处理可能的异常。
#include <iostream>
#include <stdexcept>
using namespace std ;
class SimpleResource {
private:
int* data_;
size_t size_;
public:
SimpleResource ( size_t n ) : size_ (n) {
if (n == 0 ) {
throw invalid_argument ( "大小不能为0" );
}
data_
()
<<
std
::endl;
// 可以尝试恢复操作或记录错误
}
catch ( const std ::invalid_argument & e) {
std ::cerr << "参数错误:" << e. what () << std ::endl;
// 处理参数相关的错误
}
catch ( const std ::exception & e) {
std ::cerr << "未知异常:" << e. what () << std ::endl;
// 处理其他所有标准异常
}
catch (...) {
std ::cerr << "发生了未知类型的异常" << std ::endl;
// 捕获所有类型的异常
}
}
make_unique
<
Connection
>(connectionString)) {
// 构造函数体
if ( ! conn-> isValid ()) {
throw std :: runtime_error ( "数据库连接验证失败" );
}
}
catch ( const std ::exception & e) {
std ::cerr << "数据库连接初始化失败:" << e. what () << std ::endl;
throw ; // 重新抛出异常
}
};
// 除法可能导致除零错误,不应标记为noexcept
double divide ( double a , double b ) {
if (b == 0.0 ) {
throw std :: invalid_argument ( "除数不能为零" );
}
return a / b;
}
// 移动构造函数通常应该是noexcept的
SafeCalculator ( SafeCalculator && other ) noexcept = default ;
};
}
};
// 具体的游戏异常类型
class InvalidMoveException : public GameException {
public:
explicit InvalidMoveException ( const std :: string & move )
: GameException ( "无效的移动:" + move) {}
};
class GameOverException : public GameException {
private:
std ::string winner;
public:
GameOverException ( const std :: string & winner )
: GameException ( "游戏结束" ), winner (winner) {}
const std :: string & getWinner () const { return winner; }
};
// 游戏逻辑类
class ChessGame {
public:
void makeMove ( const std :: string & move ) {
if ( ! isValidMove (move)) {
throw InvalidMoveException (move);
}
// 执行移动
executeMove (move);
if ( isGameOver ()) {
throw GameOverException ( determineWinner ());
}
}
private:
bool isValidMove ( const std :: string & move ) {
// 验证移动的逻辑
return ! move. empty () && move. length () <= 10 ;
}
void executeMove ( const std :: string & move ) {
// 执行移动的逻辑
}
bool isGameOver () {
// 判断游戏是否结束
return false ; // 简化实现
}
std :: string determineWinner () {
return "玩家A" ; // 简化实现
}
};
const
{
return
y; }
void move ( double dx , double dy ) {
x += dx;
y += dy;
}
};
class Circle {
private:
Point center;
double radius;
public:
Circle ( const Point & center , double radius )
: center (center), radius (radius) {}
double getArea () const {
return 3.14159 * radius * radius;
}
void draw () const {
// 绘制圆形的逻辑
}
};
// 辅助函数
double distance ( const Point & p1 , const Point & p2 ) {
double dx = p1. getX () - p2. getX ();
double dy = p1. getY () - p2. getY ();
return std :: sqrt (dx * dx + dy * dy);
}
}
// 音频库的命名空间
namespace Audio {
class Point {
private:
double frequency;
double amplitude;
public:
Point ( double freq , double amp ) : frequency (freq), amplitude (amp) {}
double getFrequency () const { return frequency; }
double getAmplitude () const { return amplitude; }
};
class Sound {
private:
std ::vector < Point > samples;
public:
void addSample ( const Point & sample ) {
samples. push_back (sample);
}
void play () const {
// 播放音频的逻辑
}
};
}
renderScene
() {
// 渲染场景的实现
}
void Renderer :: setViewport ( int width , int height ) {
viewportWidth = width;
viewportHeight = height;
}
void initializeGraphicsSystem () {
// 初始化图形系统的实现
}
}
(p3,
5.0
);
// 如果要使用Audio::Point,仍需完全限定
Audio ::Point p4 ( 880.0 , 0.6 );
}
void demonstrateUsingDirective () {
// using指令:引入整个命名空间
using namespace Graphics ;
Point p ( 0 , 0 ); // Graphics::Point
Circle c (p, 10.0 ); // Graphics::Circle
// 注意:如果有命名冲突,需要显式指定
Audio ::Point audioP ( 220.0 , 0.9 );
}
{
return
y; }
double getZ () const { return z; }
};
// 这个函数会被ADL找到
Vector3D add ( const Vector3D & v1 , const Vector3D & v2 ) {
return Vector3D (v1. getX () + v2. getX (),
v1. getY () + v2. getY (),
v1. getZ () + v2. getZ ());
}
void print ( const Vector3D & v ) {
std ::cout << "(" << v. getX () << ", " << v. getY () << ", " << v. getZ () << ")" ;
}
}
void demonstrateADL () {
MathUtils ::Vector3D v1 ( 1 , 2 , 3 );
MathUtils ::Vector3D v2 ( 4 , 5 , 6 );
// 即使没有using声明,这些调用也能工作
// 因为ADL会在MathUtils命名空间中查找这些函数
auto result = add (v1, v2); // 找到MathUtils::add
print (result); // 找到MathUtils::print
}
=
0
;
const std :: string & getBrand () const { return brand; }
int getYear () const { return year; }
virtual ~Vehicle () = default ;
};
// 基类:电子设备
class ElectronicDevice {
protected:
int batteryLevel;
bool isConnected;
public:
ElectronicDevice () : batteryLevel ( 100 ), isConnected ( false ) {}
virtual void turnOn () = 0 ;
virtual void turnOff () = 0 ;
void charge () { batteryLevel = 100 ; }
int getBatteryLevel () const { return batteryLevel; }
virtual ~ElectronicDevice () = default ;
};
// 多重继承:电动汽车同时是交通工具和电子设备
class ElectricCar : public Vehicle , public ElectronicDevice {
private:
double maxRange;
double currentRange;
public:
ElectricCar ( const std :: string & brand , int year , double range )
: Vehicle (brand, year), ElectronicDevice (),
maxRange (range), currentRange (range) {}
// 实现Vehicle的纯虚函数
void start () override {
if (batteryLevel > 0 ) {
std ::cout << brand << " 电动汽车启动" << std ::endl;
} else {
std ::cout << "电量不足,无法启动" << std ::endl;
}
}
void stop () override {
std ::cout << brand << " 电动汽车停止" << std ::endl;
}
// 实现ElectronicDevice的纯虚函数
void turnOn () override {
isConnected = true ;
std ::cout << "车载系统已开启" << std ::endl;
}
void turnOff () override {
isConnected = false ;
std ::cout << "车载系统已关闭" << std ::endl;
}
// 特有的方法
void drive ( double distance ) {
if (distance <= currentRange) {
currentRange -= distance;
batteryLevel = static_cast<int> ((currentRange / maxRange) * 100 );
std ::cout << "行驶了 " << distance << " 公里,剩余电量:" << batteryLevel << "%" << std ::endl;
} else {
std ::cout << "电量不足,无法完成此次行程" << std ::endl;
}
}
};
// 需要明确指定要调用哪个版本
}
// 处理命名冲突的例子
class Printer {
public:
virtual void print () { std ::cout << "打印文档" << std ::endl; }
};
class Scanner {
public:
virtual void print () { std ::cout << "打印扫描信息" << std ::endl; }
};
class MultiFunctionDevice : public Printer , public Scanner {
public:
// 解决命名冲突:提供明确的接口
void printDocument () { Printer :: print (); }
void printScanInfo () { Scanner :: print (); }
// 或者重新定义print方法
void print () override {
std ::cout << "多功能设备:选择打印模式" << std ::endl;
}
};
0
;
const std :: string & getName () const { return name; }
int getAge () const { return age; }
virtual ~Animal () = default ;
};
// 使用虚继承
class Mammal : virtual public Animal {
protected:
bool isWarmBlooded;
public:
Mammal ( const std :: string & name , int age )
: Animal (name, age), isWarmBlooded ( true ) {}
virtual void giveBirth () = 0 ;
bool getWarmBlooded () const { return isWarmBlooded; }
};
class Bird : virtual public Animal {
protected:
bool canFly;
public:
Bird ( const std :: string & name , int age , bool canFly = true )
: Animal (name, age), canFly (canFly) {}
virtual void layEgg () = 0 ;
bool getCanFly () const { return canFly; }
};
// 蝙蝠:既是哺乳动物又是飞行动物
class Bat : public Mammal , public Bird {
public:
Bat ( const std :: string & name , int age )
: Animal (name, age), // 直接初始化虚基类
Mammal (name, age),
Bird (name, age, true ) {}
void makeSound () override {
std ::cout << name << " 发出超声波" << std ::endl;
}
void move () override {
std ::cout << name << " 在夜空中飞翔" << std ::endl;
}
void giveBirth () override {
std ::cout << name << " 生下小蝙蝠" << std ::endl;
}
void layEgg () override {
// 蝙蝠不下蛋,这里可以抛出异常或提供说明
std ::cout << name << " 是哺乳动物,不会下蛋" << std ::endl;
}
};
Color c2
=
Color
::RED;
// 也正确
// 作用域枚举必须使用完全限定名
TrafficLight light = TrafficLight ::RED; // 正确
Status status = Status ::ACTIVE; // 正确
// 作用域枚举不会隐式转换为整数
int colorValue = RED; // 正确:传统枚举可以转换
// int lightValue = light; // 错误:作用域枚举不能隐式转换
int lightValue = static_cast<int> (light); // 需要显式转换
}
=
403
,
INTERNAL_ERROR = 500 ,
NETWORK_TIMEOUT = 408
};
// 位标志枚举
enum class Permission : unsigned int {
NONE = 0 ,
READ = 1 ,
WRITE = 2 ,
EXECUTE = 4 ,
DELETE = 8 ,
ALL = READ | WRITE | EXECUTE | DELETE
};
isOpen;
public:
FileManager ( const std :: string & filename , OpenMode mode , FileType type )
: filename (filename), mode (mode), type (type), isOpen ( false ) {}
bool open () {
if (isOpen) return true ;
switch (mode) {
case OpenMode ::READ_ONLY:
return openForReading ();
case OpenMode ::WRITE_ONLY:
return openForWriting ();
case OpenMode ::READ_WRITE:
return openForReadWrite ();
case OpenMode ::APPEND:
return openForAppend ();
}
return false ;
}
const char* getModeString () const {
switch (mode) {
case OpenMode ::READ_ONLY: return "只读" ;
case OpenMode ::WRITE_ONLY: return "只写" ;
case OpenMode ::READ_WRITE: return "读写" ;
case OpenMode ::APPEND: return "追加" ;
}
return "未知" ;
}
private:
bool openForReading () { /* 实现 */ return true ; }
bool openForWriting () { /* 实现 */ return true ; }
bool openForReadWrite () { /* 实现 */ return true ; }
bool openForAppend () { /* 实现 */ return true ; }
};
string
&
getName
()
const
{
return
name; }
int getAge () const { return age; }
double getGpa () const { return gpa; }
void setName ( const std :: string & n ) { name = n; }
void setAge ( int a ) { age = a; }
void setGpa ( double g ) { gpa = g; }
// 静态方法返回数据成员指针
static auto getNamePtr () { return & Student ::name; }
static auto getAgePtr () { return & Student ::age; }
static auto getGpaPtr () { return & Student ::gpa; }
};
void demonstrateDataMemberPointer () {
Student alice ( "Alice" , 20 , 3.8 );
Student bob ( "Bob" , 22 , 3.6 );
// 定义数据成员指针
auto namePtr = & Student ::name;
auto agePtr = & Student ::age;
auto gpaPtr = & Student ::gpa;
// 通过对象使用成员指针
std ::cout << "Alice的姓名:" << alice.*namePtr << std ::endl;
std ::cout << "Bob的年龄:" << bob.*agePtr << std ::endl;
// 通过指针使用成员指针
Student * studentPtr = & alice;
std ::cout << "Alice的GPA:" << studentPtr->*gpaPtr << std ::endl;
// 批量处理的例子
std ::vector < Student > students = {alice, bob};
for ( auto& student : students) {
std ::cout << "学生信息:" << student.*namePtr
<< ", 年龄:" << student.*agePtr
<< ", GPA:" << student.*gpaPtr << std ::endl;
}
}
const
{
return
a
*
b; }
double divide ( double a , double b ) const {
if (b != 0 ) return a / b;
throw std :: invalid_argument ( "除数不能为零" );
}
// 获取操作名称
const char* getOperationName ( double ( Calculator :: * op)( double , double ) const ) const {
if (op == & Calculator ::add) return "加法" ;
if (op == & Calculator ::subtract) return "减法" ;
if (op == & Calculator ::multiply) return "乘法" ;
if (op == & Calculator ::divide) return "除法" ;
return "未知操作" ;
}
};
class AdvancedCalculator {
public:
// 定义操作类型
using Operation = double ( AdvancedCalculator :: * )( double , double ) const ;
// 基本运算
double add ( double a , double b ) const { return a + b; }
double subtract ( double a , double b ) const { return a - b; }
double multiply ( double a , double b ) const { return a * b; }
double divide ( double a , double b ) const { return a / b; }
// 高级运算
double power ( double base , double exponent ) const {
return std :: pow (base, exponent);
}
double modulo ( double a , double b ) const {
return std :: fmod (a, b);
}
// 批量执行操作
std :: vector < double > executeOperations ( double a , double b ,
const std :: vector < Operation > & operations ) const {
std ::vector <double> results;
for ( const auto& op : operations) {
results. push_back (( this ->*op)(a, b));
}
return results;
}
};
void demonstrateMemberFunctionPointer () {
AdvancedCalculator calc;
// 定义操作列表
std ::vector < AdvancedCalculator ::Operation > operations = {
& AdvancedCalculator ::add,
& AdvancedCalculator ::subtract,
& AdvancedCalculator ::multiply,
& AdvancedCalculator ::divide,
& AdvancedCalculator ::power
};
double a = 10.0 , b = 3.0 ;
auto results = calc. executeOperations (a, b, operations);
std ::vector < std ::string > opNames = { "加法" , "减法" , "乘法" , "除法" , "幂运算" };
for ( size_t i = 0 ; i < results. size (); ++ i) {
std ::cout << a << " " << opNames[i] << " " << b
<< " = " << results[i] << std ::endl;
}
}
obj
,
const
MemberType
&
value
) {
obj.*memberPtr = value;
}
const MemberType & get ( const T & obj ) const {
return obj.*memberPtr;
}
};
};
class GameObject {
private:
std ::string name;
double x, y, z;
bool visible;
public:
GameObject ( const std :: string & name = "" )
: name (name), x ( 0 ), y ( 0 ), z ( 0 ), visible ( true ) {}
// 属性访问器
static PropertySystem ::Property < GameObject, std ::string > NameProperty;
static PropertySystem ::Property < GameObject, double> XProperty;
static PropertySystem ::Property < GameObject, double> YProperty;
static PropertySystem ::Property < GameObject, double> ZProperty;
static PropertySystem ::Property < GameObject, bool> VisibleProperty;
};
// 初始化静态属性
PropertySystem :: Property < GameObject , std :: string > GameObject :: NameProperty ( & GameObject :: name );
PropertySystem :: Property < GameObject , double > GameObject :: XProperty ( & GameObject :: x );
PropertySystem :: Property < GameObject , double > GameObject :: YProperty ( & GameObject :: y );
PropertySystem :: Property < GameObject , double > GameObject :: ZProperty ( & GameObject :: z );
PropertySystem :: Property < GameObject , bool > GameObject :: VisibleProperty ( & GameObject :: visible );
void demonstratePropertySystem () {
GameObject player ( "Player" );
// 使用属性系统设置值
GameObject ::NameProperty. set (player, "主角" );
GameObject ::XProperty. set (player, 100.0 );
GameObject ::YProperty. set (player, 200.0 );
GameObject ::ZProperty. set (player, 50.0 );
GameObject ::VisibleProperty. set (player, true );
// 读取属性值
std ::cout << "游戏对象: " << GameObject ::NameProperty. get (player) << std ::endl;
std ::cout << "位置: (" << GameObject ::XProperty. get (player)
<< ", " << GameObject ::YProperty. get (player)
<< ", " << GameObject ::ZProperty. get (player) << ")" << std ::endl;
std ::cout << "可见: " << ( GameObject ::VisibleProperty. get (player) ? "是" : "否" ) << std ::endl;
}
{
double x, y;
};
class Message {
private:
MessageType type;
union {
TextMessage textMsg;
NumberMessage numberMsg;
BoolMessage boolMsg;
CoordinateMessage coordMsg;
};
public:
Message ( const std :: string & text ) : type ( MessageType ::TEXT) {
std :: strncpy (textMsg.content, text. c_str (), sizeof (textMsg.content) - 1 );
textMsg.content[ sizeof (textMsg.content) - 1 ] = ' \0 ' ;
}
Message ( double value ) : type ( MessageType ::NUMBER) {
numberMsg.value = value;
}
Message ( bool flag ) : type ( MessageType ::BOOLEAN) {
boolMsg.flag = flag;
}
Message ( double x , double y ) : type ( MessageType ::COORDINATE) {
coordMsg.x = x;
coordMsg.y = y;
}
MessageType getType () const { return type; }
const TextMessage & getTextMessage () const {
if (type != MessageType ::TEXT) {
throw std :: runtime_error ( "消息类型不匹配" );
}
return textMsg;
}
const NumberMessage & getNumberMessage () const {
if (type != MessageType ::NUMBER) {
throw std :: runtime_error ( "消息类型不匹配" );
}
return numberMsg;
}
const BoolMessage & getBoolMessage () const {
if (type != MessageType ::BOOLEAN) {
throw std :: runtime_error ( "消息类型不匹配" );
}
return boolMsg;
}
const CoordinateMessage & getCoordinateMessage () const {
if (type != MessageType ::COORDINATE) {
throw std :: runtime_error ( "消息类型不匹配" );
}
return coordMsg;
}
void print () const {
switch (type) {
case MessageType ::TEXT:
std ::cout << "文本消息: " << textMsg.content << std ::endl;
break ;
case MessageType ::NUMBER:
std ::cout << "数字消息: " << numberMsg.value << std ::endl;
break ;
case MessageType ::BOOLEAN:
std ::cout << "布尔消息: " << (boolMsg.flag ? "true" : "false" ) << std ::endl;
break ;
case MessageType ::COORDINATE:
std ::cout << "坐标消息: (" << coordMsg.x << ", " << coordMsg.y << ")" << std ::endl;
break ;
}
}
};
VariantValue () : type ( Type ::NONE) {}
VariantValue ( const std :: string & s ) : type ( Type ::STRING) {
new ( & strValue) std :: string (s);
}
VariantValue ( const std :: vector < int > & v ) : type ( Type ::VECTOR) {
new ( & vecValue) std :: vector < int >(v);
}
VariantValue ( const std :: map < std :: string , int > & m ) : type ( Type ::MAP) {
new ( & mapValue) std :: map < std :: string , int >(m);
}
~VariantValue () {
reset ();
}
VariantValue ( const VariantValue & other ) : type ( Type ::NONE) {
copyFrom (other);
}
VariantValue & operator = ( const VariantValue & other ) {
if ( this != & other) {
reset ();
copyFrom (other);
}
return * this ;
}
void reset () {
switch (type) {
case Type ::STRING:
strValue. ~string ();
break ;
case Type ::VECTOR:
vecValue. ~vector ();
break ;
case Type ::MAP:
mapValue. ~map ();
break ;
case Type ::NONE:
break ;
}
type = Type ::NONE;
}
Type getType () const { return type; }
const std :: string & asString () const {
if (type != Type ::STRING) {
throw std :: runtime_error ( "类型不匹配:期望string" );
}
return strValue;
}
const std :: vector < int > & asVector () const {
if (type != Type ::VECTOR) {
throw std :: runtime_error ( "类型不匹配:期望vector" );
}
return vecValue;
}
const std :: map < std :: string , int > & asMap () const {
if (type != Type ::MAP) {
throw std :: runtime_error ( "类型不匹配:期望map" );
}
return mapValue;
}
private:
void copyFrom ( const VariantValue & other ) {
type = other.type;
switch (type) {
case Type ::STRING:
new ( & strValue) std :: string (other.strValue);
break ;
case Type ::VECTOR:
new ( & vecValue) std :: vector < int >(other.vecValue);
break ;
case Type ::MAP:
new ( & mapValue) std :: map < std :: string , int >(other.mapValue);
break ;
case Type ::NONE:
break ;
}
}
};
false
) :
reverse
(rev) {}
bool operator () ( int a , int b ) const {
if (reverse) {
return a > b; // 降序
} else {
return a < b; // 升序
}
}
};
// 局部类:定义数据处理器
class DataProcessor {
public:
static std :: vector < int > filterAndSort ( const std :: vector < int > & data ,
int threshold , bool descending = false ) {
std ::vector <int> result;
// 过滤数据
std :: copy_if (data. begin (), data. end (), std :: back_inserter (result),
[ threshold ]( int value ) { return value > threshold; });
// 排序数据
CustomComparator comp (descending);
std :: sort (result. begin (), result. end (), comp);
return result;
}
};
// 使用局部类处理数据
return DataProcessor :: filterAndSort (input, 50 , true );
}
void demonstrateLocalClass () {
std ::vector <int> data = { 10 , 60 , 30 , 80 , 20 , 90 , 40 , 70 };
auto result = processData (data);
std ::cout << "处理后的数据: " ;
for ( int value : result) {
std ::cout << value << " " ;
}
std ::cout << std ::endl;
}
}
Iterator () : current ( nullptr ) {} // 结束迭代器
int& operator * () {
if ( ! current) {
throw std :: runtime_error ( "迭代器无效" );
}
return current->data;
}
Iterator & operator ++ () {
advance ();
return * this ;
}
bool operator == ( const Iterator & other ) const {
return current == other.current;
}
bool operator != ( const Iterator & other ) const {
return ! ( * this == other);
}
private:
void pushLeftBranch ( TreeNode * node ) {
while (node) {
nodeStack. push (node);
node = node->left;
}
}
void advance () {
if (nodeStack. empty ()) {
current = nullptr ;
return ;
}
current = nodeStack. top ();
nodeStack. pop ();
if (current->right) {
pushLeftBranch (current->right);
}
}
};
// 嵌套类:树的构建器
class Builder {
private:
TreeNode * root;
public:
Builder () : root ( nullptr ) {}
Builder & addNode ( int value ) {
root = insertNode (root, value);
return * this ;
}
TreeNode * build () {
TreeNode * result = root;
root = nullptr ; // 转移所有权
return result;
}
private:
TreeNode * insertNode ( TreeNode * node , int value ) {
if ( ! node) {
return new TreeNode (value);
}
if (value < node->data) {
node->left = insertNode (node->left, value);
} else {
node->right = insertNode (node->right, value);
}
return node;
}
};
private:
int data;
TreeNode * left;
TreeNode * right;
public:
TreeNode ( int value ) : data (value), left ( nullptr ), right ( nullptr ) {}
~TreeNode () {
delete left;
delete right;
}
Iterator begin () {
return Iterator ( this );
}
Iterator end () {
return Iterator ();
}
void print () const {
if (left) left-> print ();
std ::cout << data << " " ;
if (right) right-> print ();
}
// 工厂方法:使用构建器创建树
static TreeNode * createTree ( std :: initializer_list < int > values ) {
Builder builder;
for ( int value : values) {
builder. addNode (value);
}
return builder. build ();
}
};
void demonstrateNestedClass () {
// 使用嵌套的Builder类创建树
TreeNode * tree = TreeNode :: createTree ({ 50 , 30 , 70 , 20 , 40 , 60 , 80 });
std ::cout << "中序遍历(使用嵌套Iterator类): " ;
for ( auto it = tree-> begin (); it != tree-> end (); ++ it) {
std ::cout << * it << " " ;
}
std ::cout << std ::endl;
std ::cout << "递归遍历: " ;
tree-> print ();
std ::cout << std ::endl;
delete tree;
}
num1, num2;
try {
cout << "请输入两个整数:" ;
cin >> num1 >> num2;
double result = divide (num1, num2);
cout << "结果:" << result << endl;
} catch ( const runtime_error & e) {
cout << "错误:" << e. what () << endl;
}
return 0 ;
}
异常处理让我们可以优雅地处理错误情况。throw抛出异常,try-catch捕获并处理异常。当异常被抛出时,程序会跳转到匹配的catch块,这叫做栈展开。在栈展开过程中,局部对象会被正确析构。
}
}
namespace StringLib {
string add ( const string & a , const string & b ) {
return a + b; // 字符串连接
}
size_t length ( const string & str ) {
return str. length ();
}
}
int main () {
// 使用命名空间限定符避免冲突
double result1 = MathLib :: add ( 3.5 , 2.5 );
string result2 = StringLib :: add ( "Hello" , " World" );
cout << "数学加法:" << result1 << endl;
cout << "字符串连接:" << result2 << endl;
// 也可以使用using声明
using MathLib ::multiply;
double result3 = multiply ( 4 , 5 );
cout << "乘法:" << result3 << endl;
return 0 ;
}
命名空间可以避免不同库中的同名函数发生冲突。使用命名空间::函数名的方式可以明确指定使用哪个函数。using声明可以引入特定的函数,而不需要每次都写命名空间前缀。
name_
(name),
state_
(
DeviceState
::IDLE) {}
void start () {
if (state_ == DeviceState ::IDLE) {
state_ = DeviceState ::RUNNING;
cout << name_ << " 开始运行" << endl;
} else {
cout << name_ << " 无法启动,当前状态不是空闲" << endl;
}
}
void stop () {
if (state_ == DeviceState ::RUNNING) {
state_ = DeviceState ::STOPPED;
cout << name_ << " 已停止" << endl;
}
}
string getStateName () const {
switch (state_) {
case DeviceState ::IDLE: return "空闲" ;
case DeviceState ::RUNNING: return "运行" ;
case DeviceState ::STOPPED: return "停止" ;
default : return "未知" ;
}
}
};
int main () {
Device device ( "打印机" );
cout << "当前状态:" << device. getStateName () << endl;
device. start ();
cout << "当前状态:" << device. getStateName () << endl;
device. stop ();
cout << "当前状态:" << device. getStateName () << endl;
return 0 ;
}
enum class是作用域枚举,相比传统枚举有以下优势:
枚举值不会泄漏到外层作用域,需要使用枚举名::值的方式访问
不会隐式转换为整数,类型更安全
可以指定底层类型,节省内存
()
=
0
;
virtual ~Scanner () = default ;
};
class MultiFunctionDevice : public Printer , public Scanner {
private:
string deviceName_;
public:
MultiFunctionDevice ( const string & name ) : deviceName_ (name) {}
void print ( const string & document ) override {
cout << deviceName_ << " 正在打印:" << document << endl;
}
string scan () override {
cout << deviceName_ << " 正在扫描..." << endl;
return "扫描的文档内容" ;
}
string getName () const {
return deviceName_;
}
};
int main () {
MultiFunctionDevice device ( "多功能打印机" );
// 可以作为Printer使用
Printer * printer = & device;
printer-> print ( "测试文档" );
// 可以作为Scanner使用
Scanner * scanner = & device;
string result = scanner-> scan ();
cout << "扫描结果:" << result << endl;
return 0 ;
}
多重继承允许一个类同时继承多个基类。在这个例子中,MultiFunctionDevice同时实现了Printer和Scanner接口。注意基类的析构函数应该声明为虚函数,确保通过基类指针删除对象时能正确调用派生类的析构函数。
=
new
int
[n];
for ( size_t i = 0 ; i < n; i ++ ) {
data_[i] = 0 ; // 初始化为0
}
cout << "分配了 " << n << " 个整数" << endl;
}
~SimpleResource () {
if (data_) {
delete[] data_;
cout << "释放了资源" << endl;
}
}
// 禁止拷贝(简化示例)
SimpleResource ( const SimpleResource & ) = delete ;
SimpleResource & operator = ( const SimpleResource & ) = delete ;
int& at ( size_t index ) {
if (index >= size_) {
throw out_of_range ( "索引越界!" );
}
return data_[index];
}
size_t getSize () const noexcept {
return size_;
}
};
int main () {
try {
SimpleResource resource ( 5 );
resource. at ( 0 ) = 10 ;
resource. at ( 1 ) = 20 ;
cout << "第一个元素:" << resource. at ( 0 ) << endl;
cout << "第二个元素:" << resource. at ( 1 ) << endl;
// 尝试访问越界元素
// resource.at(10) = 100; // 会抛出异常
} catch ( const exception & e) {
cout << "捕获异常:" << e. what () << endl;
}
return 0 ;
}
这个例子展示了RAII(资源获取即初始化)原则:在构造函数中获取资源,在析构函数中释放资源。即使发生异常,析构函数也会被调用,确保资源被正确释放。noexcept关键字表示函数不会抛出异常,这有助于编译器优化。
C++高级技术与实用工具 | 自在学