在大部分面向对象开发的编程语言中都有封装,继承,多态的三个特性。不管是java,python,还是C++。
官方解释:封装的意义在于将属性和行为(数据和操作数据的函数)作为一个整体,表现生活中的事物,同时保护数据成员不被类以外的程序直接访问或修改,只能通过提供的公共接口访问。
结合生活例子:想象一个闹钟。闹钟的内部有电路、电池、发声装置等。我们不关心闹钟是如何工作的,我们只关心它的功能:设置时间、响铃等。这就是封装。我们只看到闹钟给我们提供的接口(设置时间、响铃等),而其内部的具体实现细节被隐藏了。
class AlarmClock
{
private:
int hour;
int minute;
public:
void setTime(int h, int m) { hour = h; minute = m; }
void ring() { std::cout << "闹钟响了!" << std::endl; }
};
官方解释:C++继承是面向对象编程中的一个重要概念,它使得一个类可以从另一个类派生出来,从而继承了父类的属性和方法。继承提供了代码重用和多态性支持,使得程序更加灵活和可维护。
结合生活例子:想象一个家庭,家庭成员之间存在着某种关系,这种关系可以被视为一种继承。在这个家庭中,父亲是基类,而儿子和女儿是派生类,分别从父亲继承了一些样貌和行为。
class Father {
public:
string name;
int age;
string occupation;
void work()
{
cout << "Person is working." << endl;
}
void earnMoney()
{
cout << "Person is earning money." << endl;
}
void takeCareOfFamily()
{
cout << "Person is taking care of family." << endl;
}
};
class Son : public Person {
public:
void playGuitar()
{
cout << "Son is playing guitar." << endl;
}
};
class Daughter : public Person {
public:
void dance()
{
cout << "Daughter is dancing." << endl;
}
};
在C++中,继承通过使用冒号(:)关键字来实现。当一个类从另一个类派生时,子类会自动继承父类的所有公有和保护成员。子类可以拥有自己的属性和方法,也可以重写父类的方法。
C++提供了两种继承方式:公有继承(public)和私有继承(private)。
public:public成员在类外部可访问。这意味着任何包含该类对象的代码都可以访问public成员。
// 声明一个公有类
class MyClass {
public: // 公有成员在类外部可访问
// 公有构造函数
MyClass() {
// 初始化成员变量
x = 0;
y = 0;
}
// 公有成员函数
void setValues(int a, int b) {
x = a;
y = b;
}
// 公有成员函数
void printValues() {
cout << "x: " << x << ", y: " << y << endl;
}
};
class my :public MyClass
{
void manage()
{
cout << "Managing stock: " << endl;
cout << "Managing salary: " << endl;
}
}
private:private成员只能在类内部访问。这意味着只有类的成员函数可以访问private成员,而类的对象或派生类无法直接访问private成员。
// 声明一个私有类
class PrivateClass {
private: // 私有成员在类外部不可访问
int privateVar; // 私有成员变量
};
// 声明一个公有类,继承自PrivateClass
class PublicClass : public PrivateClass {
public:
void display() {
cout << "privateVar: " << privateVar << endl; // 错误!PrivateClass中的private成员不能被PublicClass继承访问
}
};
int main() {
PublicClass myObject; // 创建一个PublicClass对象
myObject.display(); // 尝试访问PrivateClass中的private成员,会导致编译错误
return 0;
}
我们有一个名为PrivateClass的私有类,其中包含一个私有成员变量privateVar。然后我们有一个名为PublicClass的公有类,它试图继承自PrivateClass。在PublicClass中,PrivateClass中的privateVar成员是不允许访问的,因为privateVar是PrivateClass的私有成员,不能被子类继承访问。
这就好比有一个小木箱,上面写着“私人物品”,你不能打开这个木箱,因为你没有权限。同样地,公有类PublicClass也不能打开私有类PrivateClass的“私人物品”箱子。通过这样的访问控制,我们可以确保数据的封装和安全性
protected关键字:protected成员在类外部不可访问,但在派生类中可访问。这意味着只有类的成员函数和派生类可以访问protected成员,而类的对象无法直接访问protected成员。
// 声明一个保护类
class Family {
protected: // 保护成员在类外部不可访问,但在派生类中可访问
string name; // 保护成员变量,表示家庭名称
int members; // 保护成员变量,表示家庭成员数量
public: // 公有成员在类外部可访问
Family()
{
name = "";
members = 0;
}
};
class BigFamily : public Family {
public: // 公有成员在类外部可访问
void describe()
{ // 描述家庭成员
cout << "In our family, we have " << members << " members. They are my grandpa, my grandma, my dad, my mom, my uncle, my aunt and me." << endl;
}
};
int main() {
BigFamily myBigFamily; // 创建一个BigFamily对象
myBigFamily.name = "Wang"; // 设置家庭名称
myBigFamily.members = 7; // 设置家庭成员数量
myBigFamily.describe(); // 描述家庭成员
return 0;
}
这个例子中,我们有一个Family类,它表示一个家庭。家庭有一些保护成员,比如name和members,这些成员在类外部不可访问,但在派生类中可访问。
我们还定义了一个BigFamily类,它继承自Family类。在BigFamily类中,我们可以访问Family类的保护成员name和members。在main函数中,我们创建了一个BigFamily对象,并对其公有和保护成员进行了操作。
多态性可以简单理解为“多种形态”。是指允许一个接口被多种形态实现,多态性是面向对象程序设计中的一个重要特性,它允许我们以统一的方式处理不同的数据类型。
比如说,你有一张画,这张画上可以画一个动物,也可以画一个植物,还可以画一个人,或者任何其他的东西。这就是多态性。
多态性可以通过虚函数和纯虚函数来实现。
1.虚函数:在基类中声明为virtual的函数。如果基类的某个函数前有virtual关键字,则派生类可以重写这个函数,此时,这个函数被称为虚函数。
class Animal {
public:
virtual void makeSound() {
cout << "The animal makes a sound" << endl;
}
};
class Derived : public Animal {
public:
void makeSound() { cout << "Derived foo" << endl; }
};
在上面的例子中,Animal作为基类,Derived作为派生类。Animal类中定义了一个以virtual的虚构函数,而Derived 继承了Animal类的makeSound函数,并重新定义函数里面的内容。
结合生活例子讲解,动物都有叫声,但是猫叫和老虎叫声又是不一样的。所以需要一个虚构函数来定义一个叫声的总函数,之后猫、老虎又各自定义自己的叫声。
2.纯虚函数
纯虚函数是C++中一个特殊的函数,它是在基类中声明为virtual,但没有实现(即没有花括号)的函数。
// 定义一个基类 "水果"
class 水果 {
public:
// 声明一个纯虚函数 "味道"
virtual void 味道() = 0; // 纯虚函数,没有默认实现
};
// 定义一个派生类 "苹果"
class 苹果 : public 水果 {
public:
// 实现基类中的纯虚函数 "味道"
void 味道() {
cout << "苹果的味道是甜的。" << endl;
}
};
// 定义一个派生类 "香蕉"
class 香蕉 : public 水果 {
public:
// 实现基类中的纯虚函数 "味道"
void 味道() {
cout << "香蕉的味道是香的。" << endl;
}
};
int main() {
// 创建一个基类指针,指向一个 "苹果" 对象
水果* fruitPtr = new 苹果();
// 通过基类指针调用纯虚函数 "味道",输出 "苹果的味道是甜的。"
fruitPtr->味道();
delete fruitPtr; // 释放内存
// 创建一个基类指针,指向一个 "香蕉" 对象
fruitPtr = new 香蕉();
// 通过基类指针调用纯虚函数 "味道",输出 "香蕉的味道是香的。"
fruitPtr->味道();
delete fruitPtr; // 释放内存
return 0;
}
为了更好地理解这个概念,我们可以举一个生活中的例子。想象一下,我们有一个抽象类“水果”,它包含一个纯虚函数“味道”。这个纯虚函数表示水果的味道,但是在抽象类中并没有给出具体的实现。如果我们尝试实例化这个抽象类,就会遇到一个问题:我们不知道这个水果的味道是什么。
因此,实例化抽象类是不被允许的。相反,我们可以创建一些派生类,如“苹果”、“香蕉”等,并为它们实现“味道”函数。这样,当我们需要一个水果对象时,就可以创建一个“苹果”或“香蕉”对象,并调用它们的“味道”函数。这样,我们就可以避免出现无法实例化抽象类的问题。
其他注意事项