目录
一、继承
二、动态内存分配
三、继承的细节
四、动态内存分配细节
五、一个动物园管理系统
继承和动态内存分配是C++中两个重要的概念
继承是C++中面向对象编程的一个重要特性,它允许我们创建一个新类,该类从现有的类中继承属性和方法,并可以添加新的属性和方法。这样做可以提高代码的重用性和可维护性。
继承的语法格式如下:
class ChildClass : public ParentClass
{
// 子类定义
};
在上述代码中,ChildClass
是子类,ParentClass
是父类。子类通过 :
连接符指定其继承自父类,并通过 public
、private
或 protected
访问控制符来指定父类的成员对子类的可见性。
继承有三种类型:公有继承、私有继承和保护继承。公有继承表示子类可以访问父类的公共成员,私有继承表示子类只能访问父类的私有成员,保护继承表示子类可以访问父类的保护成员。
在 C++ 中,动态内存分配可以通过 new
和 delete
运算符实现。动态内存分配可以使程序更加灵活,可以在程序运行时动态地分配和释放内存。
动态内存分配的基本语法如下:
new type;
delete pointer;
其中,type
表示需要分配内存的类型,可以是内置类型、指针类型或用户自定义类型。pointer
表示需要释放的指针。
使用 new
运算符分配内存时,会在堆上分配一块内存,并返回指向该内存块的指针。例如:
int* p = new int; // 动态分配一个整型变量
*p = 10; // 对指针所指向的内存进行赋值
cout << *p << endl; // 输出 10
使用 delete
运算符释放内存时,需要注意以下几点:
- 只能释放由
new
分配的内存。- 被释放的指针必须指向有效的内存。
- 避免多次释放同一块内存。
例如:
delete p; // 释放由 p 指向的内存
在使用动态内存分配时,需要注意内存泄漏和空悬指针的问题。内存泄漏指程序中分配的内存没有被及时释放,造成内存浪费;空悬指针指指向已释放的内存的指针,访问空悬指针会导致程序崩溃。
当谈到继承时,它是面向对象编程的一个重要概念。它允许我们创建一个新类,该类从现有的类中继承属性和方法,并且可以添加新的属性和方法。这种方式可以提高代码的重用性和可维护性。
在C++中,继承使用关键字class
来定义子类,并使用冒号:
来指定继承的父类。语法格式如下:
class ChildClass : public ParentClass
{
// 子类定义
};
在这个例子中,ChildClass
是子类,ParentClass
是父类。通过使用冒号连接符,我们指定子类继承自父类。public
关键字表示继承方式为公有继承。公有继承意味着子类可以访问父类的公共成员。
除了公有继承,C++还支持私有继承和保护继承。私有继承使用private
关键字,表示子类只能访问父类的私有成员。保护继承使用protected
关键字,表示子类可以访问父类的保护成员。
子类可以对继承下来的属性和方法进行覆盖或扩展。如果子类定义了与父类相同名称的成员函数,则子类的成员函数将覆盖父类的成员函数。如果子类需要调用父类的同名成员函数,可以使用作用域解析运算符::
来指定父类的名称。
是C++中另一个重要的概念,它允许在程序运行时动态地分配和释放内存。C++提供了两个相关的运算符来进行动态内存分配:new
和delete
。
使用new
运算符可以在堆上分配一块内存,并返回指向该内存块的指针。语法格式如下:
type* pointer = new type;
其中,type
表示需要分配内存的类型,可以是内置类型、指针类型或用户自定义类型。pointer
是一个指针,用于存储分配内存的起始地址。
例如,我们可以动态分配一个整型变量,并使用指针对其进行操作:
int* p = new int; // 动态分配一个整型变量
*p = 10; // 对指针所指向的内存进行赋值
使用delete
运算符可以释放通过new
分配的内存。语法格式如下:
delete pointer;
其中,pointer
是要释放的指针。注意以下几点:
new
分配的内存,不能释放栈上或全局变量的内存。例如,我们可以使用delete
释放先前动态分配的整型变量:
delete p; // 释放由p指向的内存
在使用动态内存分配时,需要注意内存泄漏和空悬指针的问题。内存泄漏指的是程序中分配的内存没有被及时释放,造成内存浪费。空悬指针是指指向已释放内存的指针,访问空悬指针可能导致程序崩溃。
这个系统可以管理动物园中的不同种类的动物,包括狮子、老虎和熊,并提供一些功能,如添加动物、显示动物列表和播放动物声音。
下面是使用C++语言实现的代码示例:
#include
#include
// 动物类
class Animal {
protected:
std::string name;
int age;
public:
// 构造函数
Animal(std::string name, int age) {
this->name = name;
this->age = age;
}
// 纯虚函数,用于播放动物声音
virtual void makeSound() = 0;
// 获取动物名称
std::string getName() {
return name;
}
// 获取动物年龄
int getAge() {
return age;
}
};
// 狮子类,继承自动物类
class Lion : public Animal {
public:
// 调用基类的构造函数
Lion(std::string name, int age) : Animal(name, age) {}
// 实现纯虚函数,播放狮子声音
void makeSound() override {
std::cout << "狮子的声音:Roar!" << std::endl;
}
};
// 老虎类,继承自动物类
class Tiger : public Animal {
public:
// 调用基类的构造函数
Tiger(std::string name, int age) : Animal(name, age) {}
// 实现纯虚函数,播放老虎声音
void makeSound() override {
std::cout << "老虎的声音:Roar!" << std::endl;
}
};
// 熊类,继承自动物类
class Bear : public Animal {
public:
// 调用基类的构造函数
Bear(std::string name, int age) : Animal(name, age) {}
// 实现纯虚函数,播放熊的声音
void makeSound() override {
std::cout << "熊的声音:Growl!" << std::endl;
}
};
// 动物园管理系统类
class ZooManagementSystem {
private:
std::vector animals; // 存储动物对象的指针容器
public:
// 添加动物
void addAnimal(Animal* animal) {
animals.push_back(animal);
}
// 显示动物列表
void displayAnimals() {
for (const auto& animal : animals) {
std::cout << "名称:" << animal->getName()
<< " 年龄:" << animal->getAge() << std::endl;
animal->makeSound();
std::cout << std::endl;
}
}
};
int main() {
// 创建动物园管理系统对象
ZooManagementSystem zms;
// 创建狮子对象并添加到动物园
Lion* lion = new Lion("Simba", 5);
zms.addAnimal(lion);
// 创建老虎对象并添加到动物园
Tiger* tiger = new Tiger("Richard", 4);
zms.addAnimal(tiger);
// 创建熊对象并添加到动物园
Bear* bear = new Bear("Bobby", 3);
zms.addAnimal(bear);
// 显示动物列表和播放声音
zms.displayAnimals();
// 释放动态分配的内存
delete lion;
delete tiger;
delete bear;
return 0;
}
在这个案例中,我们定义了一个基类Animal
和三个派生类Lion
、Tiger
和Bear
。基类Animal
包含了动物的基本属性和纯虚函数makeSound
,派生类继承了基类并实现了具体的声音。
在ZooManagementSystem
类中,我们使用一个指针容器std::vector
来存储动物对象的指针。通过使用动态内存分配的方式,我们创建了狮子、老虎和熊的对象,并通过addAnimal
方法将它们添加到动物园。然后,我们调用displayAnimals
方法显示动物的信息和播放声音。
在main
函数中,我们创建了一个ZooManagementSystem
对象,并动态分配了狮子、老虎和熊的对象。在程序结束之前,我们使用delete
运算符释放了动态分配的内存。