C++ 继承多态笔记

1 封装

封装的好处:数据隐藏;接口简化,隐藏复杂的内部;易于维护和扩展;提高代码重用性。

#include <iostream>
#include <string>

class Person {
private:
    std::string name;
    int age;
public:
    Person(const std::string& name, int age) : name(name), age(age) {} // 构造函数
    std::string getName() const {
        return name;
    }
    void setName(const std::string& newName) {
        name = newName;
    }
    int getAge() const {
        return age;
    }
    void setAge(int newAge) {
        if (newAge > 0) {
            age = newAge;
        }
    }
};
int main() {
    Person person("Alice", 30);
    std::cout << "Name: " << person.getName() << ", Age: " << person.getAge() << std::endl;
    person.setName("Bob");
    person.setAge(25);
    std::cout << "Updated Name: " << person.getName() << ", Updated Age: " << person.getAge() << std::endl;
    system("pause");
    return 0;
}

2 继承

子类拥有父类的属性、方法

class BaseStudent
{
public: // 默认是private的,所以需要指定一下
	void print() {
		cout << "BaseStudent print!" << endl;
	}
};
class DeriveStudent : public BaseStudent
{
public:
};

// 调用:
BaseStudent baseStudent;
DeriveStudent deriveStudent;
baseStudent.print();
deriveStudent.print();
BaseStudent *pBaseStudent = new DeriveStudent();
pBaseStudent->print();

// 输出结果:
BaseStudent print!
BaseStudent print!
BaseStudent print!
请按任意键继续. . .

3 多态

可以用基类的指针指向子类的对象,调用子类的方法
必要条件:有继承关系;虚函数;子类重写该虚函数

class BaseStudent
{
public:
	virtual void print() {
		cout << "BaseStudent print!" << endl;
	}
};
class DeriveStudent : public BaseStudent
{
public:
	void print() { 
		cout << "DeriveStudent print!" << endl;
	}
};
// 调用:同上
// 输出结果:
BaseStudent print!
DeriveStudent print!
DeriveStudent print!
请按任意键继续. . .

此外,析构函数最好是虚函数,保证析构时子类能够先析构:

#include<iostream>
using namespace std;

class BaseStudent
{
public: // 默认是private
	virtual void print() {
		cout << "BaseStudent print!" << endl;
	}
	virtual ~BaseStudent() {  // 为了多态时子类也能够析构,基类的析构需要是虚函数
		cout << "BaseStudent deconstructor" << endl;
	};
};
class DeriveStudent : public BaseStudent
{
public:
	void print() { // 子类重写基类的方法,基类如果是虚函数则构成多态
		cout << "DeriveStudent print!" << endl;
	}
	~DeriveStudent() {
		cout << "DeriveStudent deconstructor" << endl;
	}
};

int main()
{
	BaseStudent* pBaseStudent = new DeriveStudent();
	pBaseStudent->print();
	delete pBaseStudent;
	system("pause");
	return 0;
}

// 执行结果:
DeriveStudent print!
DeriveStudent deconstructor
BaseStudent deconstructor
请按任意键继续. . .

4 dynamic_cast用法

在C++中,dynamic_cast 用于在运行时检查对象的类型,并进行安全的向下转型。为了使 dynamic_cast 能够正确工作,基类必须至少有一个虚函数,通常是虚析构函数。这是因为 dynamic_cast 依赖于运行时类型信息(RTTI),而虚函数是实现RTTI的关键

class Base {
public:
	virtual ~Base() {}  // 虚析构函数,启用多态
};

class Derived1 : public Base {
public:
	void func1() { cout << "Derived1 function" << endl; }
};

class Derived2 : public Base {
public:
	void func2() { cout << "Derived2 function" << endl; }
};

int main()
{
	Base* b = new Derived1();
	Derived1* d1 = dynamic_cast<Derived1*>(b);
	if (d1) {
		d1->func1();
	}
	else {
		cout << "Failed to cast" << endl;
	}
	delete b;
	d1->func1();
	system("pause");
	return 0;
}
// 打印结果:
Derived1 function
Derived1 function
请按任意键继续. . .

上面的最后一句.d1->func1();这是为什么呢?d1是野指针吗?
d1 在 delete b; 之后是一个悬挂指针(dangling pointer),指向的内存已经不再有效。在这种情况下,d1 并不是一个野指针(wild pointer),因为野指针是一个未初始化的指针,而 d1 是一个已经被赋值但指向了无效内存的指针。不过,使用这样的指针同样是不安全的。

5 C++ 11构造/析构default用法

#include <iostream>
#include <string>

class MyClass {
public:
    MyClass() = default;                  // 默认构造函数
    MyClass(const MyClass&) = default;     // 默认拷贝构造函数
    MyClass(MyClass&&) = default;          // 默认移动构造函数
    MyClass& operator=(const MyClass&) = default;  // 默认拷贝赋值运算符
    MyClass& operator=(MyClass&&) = default;       // 默认移动赋值运算符
    ~MyClass() = default;                 // 默认析构函数
};

int main() {
    MyClass obj1;  // 使用默认构造函数
    MyClass obj2 = obj1;  // 使用默认拷贝构造函数
    MyClass obj3 = std::move(obj1);  // 使用默认移动构造函数
    obj1 = obj2;  // 使用默认拷贝赋值运算符
    obj1 = std::move(obj3);  // 使用默认移动赋值运算符
    system("pause");
    return 0;
}

在C++中,std::move 是一个标准库函数,它将一个对象转换为右值引用,这是为了触发移动构造函数或移动赋值运算符,而不是拷贝构造函数或拷贝赋值运算符。使用 std::move 可以显著提高性能,因为它避免了不必要的资源拷贝。

6 移动构造函数

#include <iostream>
#include <vector>

class MyClass {
public:
    std::vector<int> data;
    MyClass(MyClass&& other) : data(std::move(other.data)) {
        std::cout << "Move constructor called" << std::endl;
    }
    MyClass() {
        std::cout << "Constructor called" << std::endl;
    }
    ~MyClass() {
        std::cout << "Destructor called" << std::endl;
    }
};
MyClass createObject() {
    MyClass obj;
    obj.data.push_back(1);
    obj.data.push_back(2);
    obj.data.push_back(3);
    return obj; // 这里调用移动构造函数
}
int main() {
    MyClass myObject = createObject();
    return 0;
}

// 运行结果:
Constructor called
Move constructor called
Destructor called
请按任意键继续. . .

7 移动赋值运算符

#include <iostream>
#include <vector>

class MyClass {
public:
    std::vector<int> data;
    MyClass() { // 默认构造函数
        std::cout << "Default constructor called" << std::endl;
    }
    MyClass(MyClass&& other) : data(std::move(other.data)) { // 移动构造函数
        std::cout << "Move constructor called" << std::endl;
    }
    MyClass& operator=(MyClass&& other) {    // 移动赋值运算符
        std::cout << "Move assignment operator called" << std::endl;
        if (this != &other) { // 避免自赋值
            data = std::move(other.data);
        }
        return *this;
    }
    ~MyClass() {
        std::cout << "Destructor called" << std::endl;
    }
};

int main() {
    MyClass obj1;  // 调用默认构造函数 
    obj1.data.push_back(1);
    obj1.data.push_back(2);
    obj1.data.push_back(3);
    MyClass obj2;  // 调用默认构造函数 
    obj2 = std::move(obj1);  // 调用移动赋值运算符
    // 验证 obj1 是否被清空
    if (obj1.data.empty()) {
        std::cout << "obj1 has been moved from, and is now empty." << std::endl;
    }
    system("pause");
    return 0;
}

// 运算结果:
Default constructor called
Default constructor called
Move assignment operator called
obj1 has been moved from, and is now empty.
请按任意键继续. . .
  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值