C++ 面向对象

C++ 面向对象

C++是一名面向对象的语言。每门编程语言都有自己的编程思维,C++语言的编程思维即RAII + OOP.

创建基类Animal

/**
 * @author zsh
 * 所有类对象的成员方法均是inline方法
 * 标准写法是
 * hpp: 声明类
 * cpp: 实现类
 */
class Animal {
private:
    std::string species;
    int age;
public:
    // 默认构造函数
    Animal() : species("单细胞"), age(0){
        std::cout << "Animal Default Constructor" << "\n";
    }
    // 带参构造函数
    Animal(std::string species, int age) : species(species), age(age){
        std::cout << std::format("Animal Constructor species: {}, age: {}", species, age) << "\n";
    }
    // 拷贝构造函数
    Animal(const Animal& other) : species(other.species), age(other.age){
        std::cout << std::format("Animal Copy Constructor species: {}, age: {}", species, age) << "\n";
    }
    // 移动构造函数
    Animal(Animal&& other) noexcept : species(std::move(other.species)), age(std::exchange(other.age, 0)){
        std::cout << std::format("Animal Move Constructor species: {}, age: {}", species, age) << "\n";
    }
    // 析构函数
    virtual ~Animal(){
        std::cout << std::format("Animal Destructor species: {}, age: {}", species, age) << "\n";
    }
    // 重写赋值运算符
    Animal& operator=(const Animal& other){
        if(this != &other){
            species = other.species;
            age = other.age;
        }
        std::cout << std::format("Animal Copy Assignment Operator species: {}, age: {}", species, age) << "\n";
        return *this;
    }
    // 移动赋值运算符
    Animal& operator=(Animal&& other) noexcept {
        if(this != &other){
            species = std::move(other.species);
            age = std::exchange(other.age, 0);
        }
        std::cout << std::format("Animal Move Assignment Operator species: {}, age: {}", species, age) << "\n";
        return *this;   
    }
    // 重新<<运算符, 友元:访问成员私有变量
    friend std::ostream& operator<<(std::ostream& os, const Animal& animal) {
        os << std::format("species: {}, age: {}", animal.species, animal.age);
        return os;
    }
    // 定义为全局方法也是可以的
    // static std::ostream& operator<<(std::ostream& os, const Animal& animal) {
    //     os << std::format("species: {}, age: {}", animal.getSpecies(), animal.getAge());
    //     return os;
    // }

    /**
     * 第一个 const 代表返回不可变的值,一般和引用搭配
     * 第二个 const 代表这个函数无法修改this对象的任何属性
     */
    const std::string& getSpecies() const { 
        return species;
    }
    int getAge() const { 
        return age; 
    }
    void setSpecies(const std::string species) { 
        this->species = species; 
    }
    void setAge(const int age) { 
        this->age = age; 
    }
    // 虚函数,有虚函数,此对象仍然是一个正常可实例化的对象
    virtual void makeSound() {
        std::cout << "Some generic animal sound" << std::endl;
    };
    virtual void eat(){
        std::cout << "Animal is eating" << std::endl;
    }
    virtual void sleep(){
        std::cout << "Animal is sleeping" << std::endl;
    }
};

创建动物类,其有两个属性speciesage,代表物种,年龄.

创建Dog类型

class Dog : public Animal {
public:
    // 默认构造函数
    Dog() : Animal("狗", 0){
        std::cout << std::format("Dog Default Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 带参构造函数
    Dog(int age) : Animal("狗", age){
        std::cout << std::format("Dog Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 拷贝构造
    Dog(const Dog& other) : Animal(other.getSpecies(), other.getAge()){
        std::cout << std::format("Dog Copy Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 移动构造
    Dog(Dog&& other) noexcept : Animal(std::move(other)){ // 1
        std::cout << std::format("Dog Move Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 析构函数
    ~Dog() override {
        std::cout << std::format("Dog Destructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 拷贝赋值运算符
    Dog& operator=(const Dog& other){
        Animal::operator=(other);
        std::cout << std::format("Dog Copy Assignment Operator Species: {}, age: {}\n", getSpecies(), getAge());
        return *this;
    }
    // 移动赋值运算符
    Dog& operator=(Dog&& other) noexcept {
        Animal::operator=(std::move(other));  // 将other转为Animal&&,匹配父类移动赋值
        std::cout << std::format("Dog Move Assignment Operator species: {}, age: {}", getSpecies(), getAge()) << "\n";
        return *this;
    }
    // 狗叫
    void makeSound() override  {
        std::cout << "Woof! Woof!" << std::endl;
    }
};

代码1处的noexcept代码可以促使编译器优化,使得移动优先于拷贝.
想想数组,如果是拷贝那就得重新分配空间和赋值内容到新空间,而移动只需要将新数组的指针指向旧数组,旧数组指向null,性能嘎嘎好,所以move应该优先于copy.
注意搞清楚movecopy,不要混用两者!!!

继承访问修饰符(inheritance access specifier)。

继承后面可以跟publicprotectedprivateclass默认private, struct默认public.
总结表格
三种权限修饰符修饰后,外部访问情况。

基类成员权限 public 继承后 protected 继承后 private 继承后
public public protected private
protected protected protected private
private 不可访问 不可访问 不可访问

常用运算符

我们先看下面的代码:
代码1

Animal a1 = Dog(1);
std::cout << a1 << "\n";
/*
运行结果
Animal Constructor species: 狗, age: 1
Dog Constructor species: 狗, age: 1
Animal Move Constructor species: 狗, age: 1
Dog Destructor species: , age: 0
Animal Destructor species: , age: 0
species: 狗, age: 1
Animal Destructor species: 狗, age: 1
*/

这种并非多态,将一个Dog对象切片为Animal然后移动构造处Animal.
代码2

Dog d1;
Dog d2 = d1;
d2.setSpecies("猫");
std::cout << d1 << "\n";
std::cout << d2 << "\n";
/*
运行结果
Animal Constructor species: 狗, age: 0
Dog Default Constructor species: 狗, age: 0
Animal Constructor species: 狗, age: 0
Dog Copy Constructor species: 狗, age: 0
species: 狗, age: 0
species: 猫, age: 0
Dog Destructor species: 猫, age: 0
Animal Destructor species: 猫, age: 0
Dog Destructor species: 狗, age: 0
Animal Destructor species: 狗, age: 0
*/

代码3
触发移动赋值运算

Dog d1;
std::cout << d1 << "\n";
d1 = Dog(2);
std::cout << d1 << "\n";
/*
Animal Constructor species: 狗, age: 0
Dog Default Constructor species: 狗, age: 0
species: 狗, age: 0
Animal Constructor species: 狗, age: 2
Dog Constructor species: 狗, age: 2
Animal Move Assignment Operator species: 狗, age: 2
Dog Move Assignment Operator species: 狗, age: 2
Dog Destructor species: , age: 0
Animal Destructor species: , age: 0
species: 狗, age: 2
Dog Destructor species: 狗, age: 2
Animal Destructor species: 狗, age: 2
*/

多态

触发多态四种方法

// 1
void learn03(){
	Animal *a = new Dog(1);
	a -> makeSound();
	delete a;
}
// 2
Dog d;
Animal &a = d;
a.makeSound();
// 3
Animal &&a = Dog(1);
a.makeSound();
// 4
const Animal &a = Dog(1);
a.makeSound();

多态的条件:

  1. 基类有虚函数
  2. 子类重新虚函数
  3. 基类引用指向子类对象

代码分析:

  1. 首先在堆创建了一个Dog对象,然后将Animal的指针指向了Dog的内存,符合多态条件。
  2. 在栈空间创建了一个Dog对象,且d引用了此对象,然后a也是引用了Dog所在的内存,符合多态条件。
  3. 临时对象即右值,他的生命周期是这条语句执行结束,就会被立即回收,所以&&右值引用完全接管了这个临时对象。符合多态条件。
  4. 代码Animal &a = Dog(1);你会发现编译器报错如下cannot bind non-const lvalue reference of type 'Animal&' to an rvalue of type 'Animal',非常量左值不能绑定右值,所以改为常量左值,但是,常量左值只能调用const成员方法,所以,需要将makeSound改为const成员方法才能实现多态。

多态方法

情况1

static void playSound(Animal a) {
    a.setSpecies("playSound");
    a.makeSound();
}
void learn03(){ // 1
	Dog d(1);
	playSound(d);
}
void learn03(){ // 2
	playSound(Dog(1));
}
/*
运行结果1:
Animal Constructor species: 狗, age: 1
Dog Constructor species: 狗, age: 1
Animal Copy Constructor species: 狗, age: 1
Some generic animal sound
Animal Destructor species: playSound, age: 1
Dog Destructor species: 狗, age: 1
Animal Destructor species: 狗, age: 1
运行结果2:
Animal Constructor species: 狗, age: 1
Dog Constructor species: 狗, age: 1
Animal Move Constructor species: 狗, age: 1
Some generic animal sound
Animal Destructor species: playSound, age: 1
Dog Destructor species: , age: 0
Animal Destructor species: , age: 0
*/

可以从结果看出,只是触发了切片+拷贝,没有触发多态。
情况2:

// 代码1
static void playSound(Animal &&a) {
    a.setSpecies("playSound");
    a.makeSound();
}
void learn03(){
	playSound(Dog(1));
	std::this_thread::sleep_for(std::chrono::seconds(1));
}
// 代码2
static void playSound(Animal &a) {
    a.setSpecies("playSound");
    a.makeSound();
}
void learn03(){
	Dog d;
	playSound(d);
	std::this_thread::sleep_for(std::chrono::seconds(1));
}
// 代码3
static void playSound(const Animal &a) {
    a.makeSound();
}
void learn03(){
	playSound(Dog(1));
	std::this_thread::sleep_for(std::chrono::seconds(1));
}

可以看到多态触发了,和上面的原理一样,就不赘述了。
情况3

static Animal getAnimal() {
    Dog d(1);
    return d; // 1
}
// 被优化为
static Animal getAnimal() {
    return Dog(1); // 1
}
void learn03(){
	getAnimal().makeSound();
	std::this_thread::sleep_for(std::chrono::seconds(1));
}
/*
执行结果:
Animal Constructor species: 狗, age: 1
Dog Constructor species: 狗, age: 1
Animal Move Constructor species: 狗, age: 1
Dog Destructor species: , age: 0
Animal Destructor species: , age: 0
Some generic animal sound
Animal Destructor species: 狗, age: 1
// wait 1 seconds...
*/

从执行结果可以看出getAnimal返回的Animal对象,Dog对象是一个右值,所以将Dog右值赋值为Animal触发移动构造,打印出移动构造日志,然后调用了右值的makeSound方法,然后立马被释放掉了。

void learn03(){
	Animal a = getAnimal(); // 2
	a.makeSound();
	std::this_thread::sleep_for(std::chrono::seconds(1));
}

则会把getAnimal返回的Animal对象使用移动构造被赋值给了一个左值,其生命周期为这个函数结束其内存被释放。
这里的细节非常之多:

  1. 代码1出会发生lvalue->rvalue.所以会调用Move Constructor,所以打印出Animal Move Constructor species: 狗, age: 1
  2. 2处大多数编译器会启用 复制省略(copy elision),因此不会再额外调用构造函数。

证明

static Animal getAnimal() {
    Animal d("猫", 1);
    std::cout << d << "\n";
    return d;
}
void learn03(){
	getAnimal().makeSound();
	std::this_thread::sleep_for(std::chrono::seconds(1));
}

上面代码只调用了构造、析构函数。

虚拟继承

/**
 * @author zsh
 * 所有类对象的成员方法均是inline方法
 * 标准写法是
 * hpp: 声明类
 * cpp: 实现类
 */
class Animal {
private:
    std::string species;
    int age;
public:
    // 默认构造函数
    Animal() : species("单细胞"), age(0){
        std::cout << "Animal Default Constructor" << "\n";
    }
    // 带参构造函数
    Animal(std::string species, int age) : species(species), age(age){
        std::cout << std::format("Animal Constructor species: {}, age: {}", species, age) << "\n";
    }
    // 拷贝构造函数
    Animal(const Animal& other) : species(other.species), age(other.age){
        std::cout << std::format("Animal Copy Constructor species: {}, age: {}", species, age) << "\n";
    }
    // 移动构造函数
    Animal(Animal&& other) noexcept : species(std::move(other.species)), age(std::exchange(other.age, 0)){
        std::cout << std::format("Animal Move Constructor species: {}, age: {}", species, age) << "\n";
    }
    // 析构函数
    virtual ~Animal(){
        std::cout << std::format("Animal Destructor species: {}, age: {}", species, age) << "\n";
    }
    // 重写赋值运算符
    Animal& operator=(const Animal& other){
        if(this != &other){
            species = other.species;
            age = other.age;
        }
        std::cout << std::format("Animal Copy Assignment Operator species: {}, age: {}", species, age) << "\n";
        return *this;
    }
    // 移动赋值运算符
    Animal& operator=(Animal&& other) noexcept {
        if(this != &other){
            species = std::move(other.species);
            age = std::exchange(other.age, 0);
        }
        std::cout << std::format("Animal Move Assignment Operator species: {}, age: {}", species, age) << "\n";
        return *this;   
    }
    // 重新<<运算符, 友元:访问成员私有变量
    friend std::ostream& operator<<(std::ostream& os, const Animal& animal) {
        os << std::format("species: {}, age: {}", animal.species, animal.age);
        return os;
    }
    // 定义为全局方法也是可以的
    // static std::ostream& operator<<(std::ostream& os, const Animal& animal) {
    //     os << std::format("species: {}, age: {}", animal.getSpecies(), animal.getAge());
    //     return os;
    // }

    /**
     * 第一个 const 代表返回不可变的值,一般和引用搭配
     * 第二个 const 代表这个函数无法修改this对象的任何属性
     */
    const std::string& getSpecies() const { 
        return species;
    }
    int getAge() const { 
        return age; 
    }
    void setSpecies(const std::string species) { 
        this->species = species; 
    }
    void setAge(const int age) { 
        this->age = age; 
    }
    // 虚函数,有虚函数,此对象仍然是一个正常可实例化的对象
    virtual void makeSound() const {
        std::cout << "Some generic animal sound" << std::endl;
    };
    virtual void eat(){
        std::cout << "Animal is eating" << std::endl;
    }
    virtual void sleep(){
        std::cout << "Animal is sleeping" << std::endl;
    }
};

class Dog : virtual public Animal {
public:
    // 默认构造函数
    Dog() : Animal("狗", 0){
        std::cout << std::format("Dog Default Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 带参构造函数
    Dog(int age) : Animal("狗", age){
        std::cout << std::format("Dog Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 拷贝构造
    Dog(const Dog& other) : Animal(other.getSpecies(), other.getAge()){
        std::cout << std::format("Dog Copy Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 移动构造
    Dog(Dog&& other) noexcept : Animal(std::move(other)){
        std::cout << std::format("Dog Move Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 析构函数
    ~Dog() override {
        std::cout << std::format("Dog Destructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 拷贝赋值运算符
    Dog& operator=(const Dog& other){
        Animal::operator=(other);
        std::cout << std::format("Dog Copy Assignment Operator Species: {}, age: {}\n", getSpecies(), getAge());
        return *this;
    }
    // 移动赋值运算符
    Dog& operator=(Dog&& other) noexcept {
        Animal::operator=(std::move(other));  // 将other转为Animal&&,匹配父类移动赋值
        std::cout << std::format("Dog Move Assignment Operator species: {}, age: {}", getSpecies(), getAge()) << "\n";
        return *this;   
    }
    // 狗叫
    void makeSound() const override  {
        std::cout << "Woof! Woof!" << std::endl;
    }
};


class Cat : virtual public Animal {
public:
    // 默认构造函数
    Cat() : Animal("猫", 0){
        std::cout << std::format("Cat Default Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 带参构造函数
    Cat(int age) : Animal("猫", age){
        std::cout << std::format("Cat Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 拷贝构造
    Cat(const Cat& other) : Animal(other.getSpecies(), other.getAge()){
        std::cout << std::format("Cat Copy Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 移动构造
    Cat(Cat&& other) noexcept : Animal(std::move(other)){
        std::cout << std::format("Cat Move Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 析构函数
    ~Cat() override {
        std::cout << std::format("Cat Destructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 拷贝赋值运算符
    Cat& operator=(const Cat& other){
        Animal::operator=(other);
        std::cout << std::format("Cat Copy Assignment Operator Species: {}, age: {}\n", getSpecies(), getAge());
        return *this;
    }
    // 移动赋值运算符
    Cat& operator=(Cat&& other) noexcept {
        Animal::operator=(std::move(other));  // 将other转为Animal&&,匹配父类移动赋值
        std::cout << std::format("Cat Move Assignment Operator species: {}, age: {}", getSpecies(), getAge()) << "\n";
        return *this;   
    }
    // 猫叫
    void makeSound() const override  {
        std::cout << "Mia O! Mia O!" << std::endl;
    }
};

class CatDog : public Dog, public Cat {
public:
    // 默认构造函数
    CatDog() : Animal("猫狗", 0){
        std::cout << std::format("CatDog Default Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 带参构造函数
    CatDog(int age) : Animal("猫狗", age){
        std::cout << std::format("CatDog Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 拷贝构造
    CatDog(const CatDog& other) : Animal(other.getSpecies(), other.getAge()){
        std::cout << std::format("CatDog Copy Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 移动构造
    CatDog(CatDog&& other) noexcept : Animal(std::move(other)){
        std::cout << std::format("CatDog Move Constructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 析构函数
    ~CatDog() override {
        std::cout << std::format("CatDog Destructor species: {}, age: {}\n", getSpecies(), getAge());
    }
    // 拷贝赋值运算符
    CatDog& operator=(const CatDog& other){
        Animal::operator=(other);
        std::cout << std::format("CatDog Copy Assignment Operator Species: {}, age: {}\n", getSpecies(), getAge());
        return *this;
    }
    // 移动赋值运算符
    CatDog& operator=(CatDog&& other) noexcept {
        Animal::operator=(std::move(other));  // 将other转为Animal&&,匹配父类移动赋值
        std::cout << std::format("CatDog Move Assignment Operator species: {}, age: {}", getSpecies(), getAge()) << "\n";
        return *this;   
    }
    // 猫狗叫
    void makeSound() const override  {
        std::cout << "Mia Woof! Mia Woof!" << std::endl;
    }
};

namespace learn01{
    void learn03(){
        CatDog c(1);
        std::cout << c << "\n";
    }
}
/*
运行结果
Animal Constructor species: 猫狗, age: 1
Dog Default Constructor species: 猫狗, age: 1
Cat Default Constructor species: 猫狗, age: 1
CatDog Constructor species: 猫狗, age: 1
species: 猫狗, age: 1
CatDog Destructor species: 猫狗, age: 1
Cat Destructor species: 猫狗, age: 1
Dog Destructor species: 猫狗, age: 1
Animal Destructor species: 猫狗, age: 1
*/

在没有菱形继承的情况下,无需使用虚拟继承。
如果存在菱形继承的情况下,需要使用虚拟继承。以CatDog为例,虚拟继承保留了CatDog自由实现方法的权利,它可以决定复用AnimalDogCat的成员函数,或者自己重写。

posted @ 2025-09-07 15:53  爱情丶眨眼而去  阅读(21)  评论(0)    收藏  举报