相关内容:
- 在
C++
中,可以将虚函数声明为纯虚函数,语法格式为:virtual 返回值类型 函数名 (函数参数) = 0;
- 纯虚函数没有函数体,只有函数声明,在虚函数声明的结尾加上
=0
,表明此函数为纯虚函数。最后的=0
并不表示函数返回值为0
,它只起形式上的作用,告诉编译系统“这是纯虚函数”。 - 包含纯虚函数的类称为抽象类
(Abstract Class)
。之所以说它抽象,是因为它无法实例化,也就是无法创建对象。原因很明显,纯虚函数没有函数体,不是完整的函数,无法调用,也无法为其分配内存空间。 - 抽象类通常是作为基类,让派生类去实现纯虚函数。派生类必须实现纯虚函数才能被实例化。抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,也就是说抽象类是为派生类服务的。纯虚函数作为基类中的一个接口,该函数会根据不同的派生类具有不同的实现方式。
- 一个纯虚函数就可以使类成为抽象基类,但是抽象基类中除了包含纯虚函数外,还可以包含其它的成员函数(虚函数或普通函数)和成员变量。
- 只有类中的虚函数才能被声明为纯虚函数,普通成员函数和顶层函数均不能声明为纯虚函数。
使用抽象类时注意:
- 抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。
- 抽象类是不能定义对象的。
引入的原因:
- 为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
- 在很多情况下,基类本身生成对象是不合情理的。
例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。 为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数,则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。声明了纯虚函数的类是一个抽象类。所以,用户不能创建抽象类的实例,只能创建它的派生类(实现了基类中的纯虚函数的定义)的实例。纯虚函数最显著的特征是:它们必须在继承类中重新声明函数(不要后面的=0,否则该派生类也不能实例化),而且它们在抽象类中往往没有定义。定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。纯虚函数的意义,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的缺省实现。所以类纯虚函数的声明就是在告诉子类的设计者,“你必须提供一个纯虚函数的实现,但我不知道你会怎样实现它”。虚函数的定义:
- 虚函数是相对于类来说的,只有类中的成员函数才可能是虚函数,另外需要注意,内联函数、构造函数、静态函数不能是虚函数。
virtual void func(){/*---*/}
此表达需要在父类子类中同时声明和定义,(实现的方式可以不同)。- 虚函数主要是实现了多态,当定义一个父类的指针指向子类对象时,虚函数的执行结果可以是子类中虚函数的定义。
子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的(注意,再同一个类中,函数重载是根据函数参数个数和类型的不同在程序编译期间决定,称为静态联编)。 - 静态联编:在程序链接阶段就可以确定的调用。
- 动态联编:在程序执行时才能确定的调用。
代码相关内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
using namespace std;
// 这是一个抽象类,也叫抽象数据类型(包含一个或者多个纯虚函数的类即抽象类)
class Shape
{
public:
Shape(){}
virtual ~Shape() {} // 虚函数
virtual double GetArea() = 0; // 纯虚函数
virtual double GetPerim() = 0; // 纯虚函数
virtual void Draw() = 0; // 纯虚函数
};
//因为这是一个纯虚函数,可以写也可以不写,一般都不写!
void Shape::Draw() //如果写,就要写一些有用的,这样在此基类的派生类中都可以调用这个函数;
{
cout << "Abstract drawing 方法!\n";
}
class Circle : public Shape
{
public:
Circle(int radius) : itsRadius(radius) {}
virtual ~Circle() {} // 只要有一个成员函数是虚函数,都要把析构函数定义为虚函数;
double GetArea() { return 3.14 * itsRadius * itsRadius; }
double GetPerim() { return 2 * 3.14 * itsRadius; }
void Draw();
private:
int itsRadius;
};
void Circle::Draw()
{
cout << "Circle drawing routine here!\n";
Shape::Draw();
}
class Rectangle : public Shape
{
public:
Rectangle(int len, int width):itsWidth(width),itsLength(len) {}
virtual ~Rectangle() {}
double GetArea() { return itsLength * itsWidth; }
double GetPerim() { return 2 * itsLength*itsWidth; }
virtual int GetLength() { return itsLength; }
virtual int GetWidth() { return itsWidth; }
void Draw();
private:
int itsWidth;
int itsLength;
};
void Rectangle::Draw()
{
for (int i = 0; i < itsLength; i++)
{
for (int j = 0; j < itsLength; j++)
cout << "x ";
cout << "\n";
}
Shape::Draw();
}
class Square : public Rectangle
{
public:
Square(int len);
Square(int len, int width);
virtual ~Square() {}
double GetPerim() { return 4 * GetLength(); }
};
Square::Square(int len):Rectangle(len,len){} // 调用基类的函数来实现;
Square::Square(int len, int width):Rectangle(len,width)
{
if (GetLength() != GetWidth())
cout << "Error,not a square...a Rectangle??\n";
}
int main()
{
//Shape a;// 错误,抽象类不能实例化;
/* //测试代码:以下代码正确;
Circle a(8);//因为派生类Circle将基类Shape中的纯虚函数都进行重写了,所以Circle不再是抽象类,所以可以实例化;
a.Draw();
Rectangle b(5, 10);
b.Draw();
cout << endl;
Square c(8);
c.Draw();
*/
int choice;
bool fQuit = false;
Shape *sp = nullptr;
while (fQuit == false)
{
cout << "(1)Circle (2)Rectangle (3)Square (0)Quit: ";
cin >> choice;
switch (choice)
{
case 1:
sp = new Circle(5); //如果是指针来创建对象需要用new来进行创建;
break;
case 2:
sp = new Rectangle(4, 6);
break;
case 3:
sp = new Square(5);
break;
case 0:
fQuit = true;
break;
}
if (fQuit == false)
{
sp->Draw();
delete sp; // 对于new的对象,最后要delete;
cout << endl;
}
}
return 0;
}代码执行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
using namespace std;
enum COLOR {Red, Green, Blue, Yellow, White, Black, Brown};
class Animal
{
public:
Animal(int);
virtual ~Animal() { cout << "Animal的析构函数被调用...\n"; }
virtual int GetAge() const { return itsAge; }
virtual void SetAge(int age) { itsAge = age; }
virtual void Sleep() const = 0;
virtual void Eat() const = 0;
virtual void Reproduce() const = 0;//生孩子
virtual void Move() const = 0;
virtual void Speak() const = 0;
private:
int itsAge;
};
Animal::Animal(int age):itsAge(age)
{
cout << "Animal的构造函数被调用...\n";
}
class Mammal : public Animal
{
public:
Mammal(int age) :Animal(age)
{
cout << "Mammal构造函数被调用...\n";
}
virtual ~Mammal() { cout << "Mammal析构函数被调用...\n"; }
// 此处只是重写了一个纯虚函数,还有4个纯虚函数没有重写,所以另外4个继承下来之后还是纯虚函数;所以Mammal还是抽象类;
virtual void Reproduce() const
{
cout << "Mammal reproduction depicted...\n";
}
};
class Fish : public Animal
{
public:
Fish(int age) :Animal(age)
{
cout << "Fish构造函数被调用...\n";
}
virtual ~Fish()
{
cout << "Fish析构函数被调用...\n";
}
virtual void Sleep() const { cout << "fish snoring...\n"; }
virtual void Eat() const { cout << "fish feeding ...\n"; }
virtual void Reproduce() const { cout << "fish laying eggs...\n"; }
virtual void Move() const { cout << "fish swimming...\n"; }
virtual void Speak() const {}
};
class Horse : public Mammal
{
public:
Horse(int age, COLOR color) :Mammal(age), itsColor(color)
{
cout << "Horse构造函数被调用...\n";
}
virtual ~Horse()
{
cout << "Horse析构函数被调用...\n";
}
//此处没有重写Reproduce,所以Reproduce函数使用继承自Mammal类的函数;
virtual void Speak() const { cout << "Whinny!...\n"; }
virtual void Sleep() const { cout << "Horse snoring...\n"; }
virtual COLOR GetItsColor() const { return itsColor; }
virtual void Move() const { cout << "Horse runing...\n"; }
virtual void Eat() const { cout << "Horse feeding ...\n"; }
protected:
COLOR itsColor;
};
class Dog : public Mammal
{
public:
Dog(int age, COLOR color) :Mammal(age), itsColor(color)
{
cout << "Dog构造函数被调用...\n";
}
virtual ~Dog()
{
cout << "Dog析构函数被调用...\n";
}
virtual void Speak() const { cout << "Whoof!...\n"; }
virtual void Sleep() const { cout << "Dog snoring... \n"; }
virtual void Eat() const { cout << "Dog eating...\n"; }
virtual void Move() const { cout << "Dog runing...\n"; }
virtual void Reproduce() const
{
cout << "Dogs reproducing...\n";
}
protected:
COLOR itsColor;
};
int main()
{
Animal *pAnimal = 0;
int choice;
bool fQuit = false;
while (fQuit == false)
{
cout << "(1)Dog (2)Horse (3)Fish (0)Quit: ";
cin >> choice;
switch (choice)
{
case 1:
pAnimal = new Dog(5, Brown);
break;
case 2:
pAnimal = new Horse(4, Black);
break;
case 3:
pAnimal = new Fish(5);
break;
default:
fQuit = true;
break;
}
if (fQuit == false)
{
pAnimal->Speak();
pAnimal->Eat();
pAnimal->Reproduce();
pAnimal->Move();
pAnimal->Sleep();
delete pAnimal; // 每次new的对象使用完之后,一定要delete,否则会出现内存泄漏的严重问题;
cout << endl;
}
}
return 0;
}代码执行结果:
参考链接:
error C4703: 使用了可能未初始化的本地指针变量“xxx”
C++纯虚函数和抽象类详解
抽象类、纯虚函数、虚函数