c++的笔记

文章目录

    • 友元
      • 关键点说明:
      • 运行输出:
      • 友元类的例子
    • 使用友元的情况
      • 1. **需要外部函数直接操作类的私有成员**
        • 示例场景:打印类的数据
      • 2. **两个类之间需要紧密协作**
        • 示例场景:管理类和数据类
      • 3. **操作符重载需要访问私有成员**
        • 示例场景:重载输出操作符
      • 4. **测试或调试需要访问内部状态**
        • 示例场景:调试工具
      • 注意事项和使用建议
      • 总结
    • 继承
      • 输出:
      • 关键点解释
        • 1. **基类(Animal)**
        • 2. **派生类(Dog)**
        • 3. **继承类型**
        • 4. **方法重写**
    • using type = int;
      • 类型别名基本用法
        • 输出:
        • 特点:
      • 与虚函数结合的示例
        • 输出:
      • 代码中的关键点
      • `using` vs `typedef`
        • 优势:
      • 使用场景

下面是一个简单的C++面向对象编程示例,展示类、对象和基本成员的使用:

#include 
#include 
#include  

using namespace std;

// 定义一个简单的类
class Person {
private:
    string name;    // 私有成员变量
    int age;

public:
    // 构造函数
    Person(string n, int a) {
        name = n;
        age = a;
    }

    // 成员函数
    void introduce() {
        cout << "Hello, my name is " << name 
             << " and I am " << age << " years old." << endl;
    }
};

int main() {
    SetConsoleOutputCP(CP_UTF8);  // 设置控制台为 UTF-8 编码

    // 创建对象
    Person person1("Alice", 25);
    
    // 调用成员函数
    person1.introduce();
    
    return 0;
}

这个程序展示了C++面向对象的基本特性:

  1. 类定义 (class Person):包含私有数据成员和公有成员函数
  2. 封装:name 和 age 是私有的,只能通过类的成员函数访问
  3. 构造函数:用于初始化对象
  4. 对象创建:使用类创建具体实例
  5. 成员函数:定义对象的行为

运行输出:

Hello, my name is Alice and I am 25 years old.

友元

下面是一个展示C++中友元(friend)功能的简单示例。友元允许特定的函数或类访问另一个类的私有成员:

#include 
#include 

using namespace std;

class Person {
private:
    string name;
    int age;

public:
    // 构造函数
    Person(string n, int a) {
        name = n;
        age = a;
    }

    // 声明友元函数
    friend void showInfo(Person p);
};

// 友元函数定义,可以访问Person的私有成员
void showInfo(Person p) {
    cout << "Name: " << p.name << ", Age: " << p.age << endl;
}

int main() {
    // 创建对象
    Person person1("Bob", 30);
    
    // 调用友元函数
    showInfo(person1);
    
    return 0;
}

关键点说明:

  1. 友元声明:在 Person 类中通过 friend void showInfo(Person p); 声明 showInfo 为友元函数。
  2. 访问权限:尽管 nameage 是私有的,showInfo 函数仍能直接访问它们,因为它是友元。
  3. 使用场景:友元通常用于需要打破封装但又必须访问私有成员的情况。

运行输出:

Name: Bob, Age: 30

友元类的例子

如果你想要看友元类的用法,这里是一个扩展示例:

#include 
#include 

using namespace std;

class Person {
private:
    string name;
    int age;

public:
    Person(string n, int a) : name(n), age(a) {}

    // 声明另一个类为友元
    friend class InfoPrinter;
};

// 友元类
class InfoPrinter {
public:
    void display(Person p) {
        cout << "Name: " << p.name << ", Age: " << p.age << endl;
    }
};

int main() {
    Person person1("Alice", 25);
    InfoPrinter printer;
    
    printer.display(person1);
    
    return 0;
}

在这个例子中,InfoPrinter 类是 Person 的友元类,因此它可以访问 Person 的私有成员。输出与之前相同,但展示了类级别的友元关系。

使用友元的情况


1. 需要外部函数直接操作类的私有成员

当一个独立的函数需要访问类的私有数据,且不适合作为类的成员函数时,可以使用友元函数。

示例场景:打印类的数据
#include 
#include 

using namespace std;

class Box {
private:
    double width;
    double height;

public:
    Box(double w, double h) : width(w), height(h) {}

    // 声明友元函数
    friend void printArea(Box b);
};

// 友元函数直接访问私有成员
void printArea(Box b) {
    double area = b.width * b.height;
    cout << "Area: " << area << endl;
}

int main() {
    Box box(5.0, 3.0);
    printArea(box);
    return 0;
}

使用情况printArea 不属于 Box 类,但需要计算和访问其私有成员。将其声明为友元避免了添加额外的公有 getter 函数。


2. 两个类之间需要紧密协作

当两个类相互依赖,并且一个类需要访问另一个类的私有成员时,可以将其中一个类声明为另一个类的友元。

示例场景:管理类和数据类
#include 
#include 

using namespace std;

class Employee {
private:
    string name;
    double salary;

public:
    Employee(string n, double s) : name(n), salary(s) {}

    // 声明 Manager 为友元类
    friend class Manager;
};

class Manager {
public:
    void adjustSalary(Employee& e, double newSalary) {
        e.salary = newSalary;  // 直接访问私有成员
        cout << e.name << "'s new salary: " << e.salary << endl;
    }
};

int main() {
    Employee emp("John", 50000);
    Manager mgr;
    mgr.adjustSalary(emp, 60000);
    return 0;
}

使用情况Manager 需要直接修改 Employee 的私有成员 salary,通过友元关系实现紧密协作。


3. 操作符重载需要访问私有成员

在重载操作符(如 +, <<)时,如果操作符函数不是类的成员函数,通常需要访问类的私有数据,这时可以声明为友元。

示例场景:重载输出操作符
#include 
#include 

using namespace std;

class Point {
private:
    int x;
    int y;

public:
    Point(int xVal, int yVal) : x(xVal), y(yVal) {}

    // 声明友元函数重载 <<
    friend ostream& operator<<(ostream& os, const Point& p);
};

// 友元函数实现
ostream& operator<<(ostream& os, const Point& p) {
    os << "(" << p.x << ", " << p.y << ")";
    return os;
}

int main() {
    Point p(3, 4);
    cout << p << endl;
    return 0;
}

使用情况<< 操作符需要访问 Point 的私有成员 xy,通过友元实现简洁的输出。


4. 测试或调试需要访问内部状态

在开发过程中,为了测试类的私有成员状态,可以临时使用友元函数或友元类。

示例场景:调试工具
#include 

using namespace std;

class System {
private:
    int status;

public:
    System(int s) : status(s) {}

    // 声明友元函数用于调试
    friend void debugStatus(System sys);
};

void debugStatus(System sys) {
    cout << "System status: " << sys.status << endl;
}

int main() {
    System sys(42);
    debugStatus(sys);
    return 0;
}

使用情况debugStatus 用于检查 System 的内部状态,便于调试。


注意事项和使用建议

  • 谨慎使用:友元打破了封装性,可能导致代码维护困难。通常优先考虑使用公有接口(getter/setter)或成员函数。
  • 替代方案:如果只是简单的数据访问,可以用公有方法代替;如果涉及复杂逻辑,可以考虑继承或组合。
  • 典型应用:友元在操作符重载、紧密相关的类(如容器和迭代器)以及特殊工具函数中较为常见。

总结

友元的使用情况主要集中在:

  1. 外部函数需要访问私有数据(如打印、计算)
  2. 类间紧密协作(如管理类和数据类)
  3. 操作符重载(如 <<, +
  4. 测试和调试需求

继承

下面是一个展示C++继承(Inheritance)基本概念的简单示例,包括基类、派生类和访问控制:

父类继承下来,会继承三样东西:
1.成员变量
2.成员函数
3.类型定义

#include 
#include 

using namespace std;

// 基类
class Animal {
protected:  // protected成员可被派生类访问
    string name;
    int age;

public:
    Animal(string n, int a) : name(n), age(a) {}

    void eat() {
        cout << name << " is eating." << endl;
    }
};

// 派生类(公有继承)
class Dog : public Animal {
private:
    string breed;

public:
    // 构造函数,调用基类构造函数
    Dog(string n, int a, string b) : Animal(n, a), breed(b) {}

    void bark() {
        cout << name << " (a " << breed << ") is barking!" << endl;
    }

    // 重写基类方法
    void eat() {
        cout << name << " (a " << breed << ") is eating dog food." << endl;
    }
};

int main() {
    // 创建基类对象
    Animal animal("Generic", 5);
    animal.eat();

    // 创建派生类对象
    Dog dog("Max", 3, "Golden Retriever");
    dog.eat();   // 调用重写后的方法
    dog.bark();  // 调用派生类特有方法

    return 0;
}

输出:

Generic is eating.
Max (a Golden Retriever) is eating dog food.
Max (a Golden Retriever) is barking!

关键点解释

1. 基类(Animal)
  • 定义了通用的属性(name, age)和行为(eat())。
  • 使用 protected 访问修饰符,使派生类可以访问这些成员。
2. 派生类(Dog)
  • 使用 public 继承方式从 Animal 继承。
  • 继承了基类的 nameage,并添加了自己的属性 breed
  • 通过构造函数初始化列表调用基类构造函数:Animal(n, a)
3. 继承类型
  • 公有继承(public):基类的公有成员在派生类中仍为公有,受保护成员仍为受保护。
  • 私有继承(private):基类的公有和受保护成员在派生类中变为私有。
  • 受保护继承(protected):基类的公有成员变为受保护,受保护成员保持不变。
4. 方法重写
  • Dog 类重写了基类的 eat() 方法,提供了更具体的实现。
  • 基类的原始 eat() 方法被覆盖,但仍可通过作用域解析运算符(如 Animal::eat())调用。

using type = int;

你提到的 using type = int; 是C++11引入的类型别名(Type Alias)语法,用于为现有类型定义一个新的名称。它类似于 typedef,但更灵活且可读性更好。下面我将解释它的用法,并结合虚函数和多态的上下文提供一个相关示例。


类型别名基本用法

#include 

using namespace std;

// 类型别名
using type = int;  // "type" 现在是 "int" 的别名

int main() {
    type number = 42;  // 等价于 int number = 42;
    cout << "Number: " << number << endl;
    return 0;
}
输出:
Number: 42
特点:
  • using type = int; 定义 typeint 的别名。
  • typedef int type; 等价,但 using 语法更直观,且支持模板。

与虚函数结合的示例

假设我们在一个多态场景中使用类型别名来简化代码:

#include 
#include 
#include 

using namespace std;

// 类型别名
using Distance = double;  // 为 double 定义别名

// 基类
class Vehicle {
protected:
    string name;

public:
    Vehicle(string n) : name(n) {}

    // 虚函数
    virtual Distance calculateFuelEfficiency() const {
        return 0.0;  // 默认实现
    }

    virtual ~Vehicle() {}
};

// 派生类1
class Car : public Vehicle {
private:
    Distance miles;
    Distance gallons;

public:
    Car(string n, Distance m, Distance g) : Vehicle(n), miles(m), gallons(g) {}

    Distance calculateFuelEfficiency() const override {
        return miles / gallons;  // 计算每加仑英里数
    }
};

// 派生类2
class Bike : public Vehicle {
private:
    Distance miles;
    Distance liters;

public:
    Bike(string n, Distance m, Distance l) : Vehicle(n), miles(m), liters(l) {}

    Distance calculateFuelEfficiency() const override {
        return miles / liters;  // 计算每升英里数
    }
};

int main() {
    vector<Vehicle*> vehicles;
    vehicles.push_back(new Car("Toyota", 300.0, 10.0));
    vehicles.push_back(new Bike("Harley", 120.0, 2.0));

    for (const auto* v : vehicles) {
        Distance efficiency = v->calculateFuelEfficiency();
        cout << "Fuel efficiency: " << efficiency << " miles per unit" << endl;
    }

    for (auto* v : vehicles) {
        delete v;
    }

    return 0;
}
输出:
Fuel efficiency: 30 miles per unit
Fuel efficiency: 60 miles per unit

代码中的关键点

  1. 类型别名(using Distance = double;

    • double 定义为 Distance,提高了代码的可读性,表示这是一个距离或效率相关的类型。
    • 在类中统一使用 Distance,如果将来需要更改类型(如改为 float),只需改别名定义。
  2. 虚函数(calculateFuelEfficiency

    • 基类提供默认实现,返回类型为 Distance
    • 派生类 CarBike 重写该函数,计算不同的燃料效率。
  3. 运行时多态

    • 通过 Vehicle* 指针调用 calculateFuelEfficiency(),运行时根据对象类型执行对应的实现。

using vs typedef

  • typedef(C++98):
    typedef double Distance;  // 传统方式
    
  • using(C++11):
    using Distance = double;  // 现代方式
    
优势:
  • using 支持模板别名,而 typedef 不支持:
    template<typename T>
    using Ptr = T*;  // 模板别名
    Ptr<int> p = nullptr;  // int* p
    
  • using 语法更直观,类似于赋值。

使用场景

  1. 提高可读性
    • Distance 表示距离相关的变量,而不是直接用 double
  2. 类型统一
    • 在大型项目中,统一类型别名便于维护。
  3. 模板编程
    • using 在模板中更灵活,如定义容器别名:
      template<typename T>
      using Vec = std::vector<T>;
      Vec<int> v;  // 等价于 std::vector
      

你可能感兴趣的:(c++,笔记,开发语言)