C++ polymorphism Virtual Function 多态 虚函数 接口 抽象类 纯虚函数
Polymorphism in C++ https://www.tutorialspoint.com/cplusplus/cpp_polymorphism.htm
https://github.com/mongodb/mongo/blob/410656e971aff8f491a87337a17d04bd866389ba/src/mongo/base/initializer.cpp
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 147 148 | /** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Server Side Public License for more details. * * You should have received a copy of the Server Side Public License * along with this program. If not, see * <http://www.mongodb.com/licensing/server-side-public-license>. * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the Server Side Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/platform/basic.h" #include "mongo/base/initializer.h" #include <iostream> #include "mongo/base/deinitializer_context.h" #include "mongo/base/global_initializer.h" #include "mongo/base/initializer_context.h" #include "mongo/util/assert_util.h" #include "mongo/util/quick_exit.h" namespace mongo { Initializer::Initializer() {} Initializer::~Initializer() {} Status Initializer::executeInitializers( const InitializerContext::ArgumentVector& args, const InitializerContext::EnvironmentMap& env) { std::vector<std::string> sortedNodes; Status status = _graph.topSort(&sortedNodes); if (Status::OK() != status) return status; InitializerContext context(args, env); for ( size_t i = 0; i < sortedNodes.size(); ++i) { InitializerDependencyNode* node = _graph.getInitializerNode(sortedNodes[i]); // If already initialized then this node is a legacy initializer without re-initialization // support. if (node->isInitialized()) continue ; auto const & fn = node->getInitializerFunction(); if (!fn) { return Status(ErrorCodes::InternalError, "topSort returned a node that has no associated function: \"" + sortedNodes[i] + '"' ); } try { status = fn(&context); } catch ( const DBException& xcp) { return xcp.toStatus(); } if (Status::OK() != status) return status; node->setInitialized( true ); } return Status::OK(); } Status Initializer::executeDeinitializers() { std::vector<std::string> sortedNodes; Status status = _graph.topSort(&sortedNodes); if (Status::OK() != status) return status; DeinitializerContext context{}; // Execute deinitialization in reverse order from initialization. for ( auto it = sortedNodes.rbegin(), end = sortedNodes.rend(); it != end; ++it) { InitializerDependencyNode* node = _graph.getInitializerNode(*it); auto const & fn = node->getDeinitializerFunction(); if (fn) { try { status = fn(&context); } catch ( const DBException& xcp) { return xcp.toStatus(); } if (Status::OK() != status) return status; node->setInitialized( false ); } } return Status::OK(); } Status runGlobalInitializers( const InitializerContext::ArgumentVector& args, const InitializerContext::EnvironmentMap& env) { return getGlobalInitializer().executeInitializers(args, env); } Status runGlobalInitializers( int argc, const char * const * argv, const char * const * envp) { InitializerContext::ArgumentVector args(argc); std::copy(argv, argv + argc, args.begin()); InitializerContext::EnvironmentMap env; if (envp) { for (; *envp; ++envp) { const char * firstEqualSign = strchr (*envp, '=' ); if (!firstEqualSign) { return Status(ErrorCodes::BadValue, "malformed environment block" ); } env[std::string(*envp, firstEqualSign)] = std::string(firstEqualSign + 1); } } return runGlobalInitializers(args, env); } Status runGlobalDeinitializers() { return getGlobalInitializer().executeDeinitializers(); } void runGlobalInitializersOrDie( int argc, const char * const * argv, const char * const * envp) { Status status = runGlobalInitializers(argc, argv, envp); if (!status.isOK()) { std::cerr << "Failed global initialization: " << status << std::endl; quickExit(1); } } } // namespace mongo |
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 | #include <iostream> using namespace std; class Shape { protected : int width, height; public : Shape( int a = 0, int b = 0){ width = a; height = b; } int area() { cout << "Parent class area :" <<endl; return 0; } }; class Rectangle: public Shape { public : Rectangle( int a = 0, int b = 0):Shape(a, b) { } int area () { cout << "Rectangle class area :" <<endl; return (width * height); } }; class Triangle: public Shape { public : Triangle( int a = 0, int b = 0):Shape(a, b) { } int area () { cout << "Triangle class area :" <<endl; return (width * height / 2); } }; // Main function for the program int main() { Shape *shape; Rectangle rec(10,7); Triangle tri(10,5); // store the address of Rectangle shape = &rec; // call rectangle area. shape->area(); // store the address of Triangle shape = &tri; // call triangle area. shape->area(); return 0; } |
Polymorphism in C++ https://www.tutorialspoint.com/cplusplus/cpp_polymorphism.htm
C++ 多态 | 菜鸟教程 http://www.runoob.com/cplusplus/cpp-polymorphism.html
C++ 多态
多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
下面的实例中,基类 Shape 被派生为两个类,如下所示:
实例
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 | #include <iostream> using namespace std; class Shape { protected : int width, height; public : Shape( int a=0, int b=0) { width = a; height = b; } int area() { cout << "Parent class area :" <<endl; return 0; } }; class Rectangle: public Shape{ public : Rectangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Rectangle class area :" <<endl; return (width * height); } }; class Triangle: public Shape{ public : Triangle( int a=0, int b=0):Shape(a, b) { } int area () { cout << "Triangle class area :" <<endl; return (width * height / 2); } }; // 程序的主函数 int main( ) { Shape *shape; Rectangle rec(10,7); Triangle tri(10,5); // 存储矩形的地址 shape = &rec; // 调用矩形的求面积函数 area shape->area(); // 存储三角形的地址 shape = &tri; // 调用三角形的求面积函数 area shape->area(); return 0; } |
当上面的代码被编译和执行时,它会产生下列结果:
Parent class area
Parent class area
导致错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。
但现在,让我们对程序稍作修改,在 Shape 类中,area() 的声明前放置关键字 virtual,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Shape { protected : int width, height; public : Shape( int a=0, int b=0) { width = a; height = b; } virtual int area() { cout << "Parent class area :" <<endl; return 0; } }; |
修改后,当编译和执行前面的实例代码时,它会产生以下结果:
Rectangle class area
Triangle class area
此时,编译器看的是指针的内容,而不是它的类型。因此,由于 tri 和 rec 类的对象的地址存储在 *shape 中,所以会调用各自的 area() 函数。
正如您所看到的,每个子类都有一个函数 area() 的独立实现。这就是多态的一般使用方式。有了多态,您可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。
虚函数
虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。
纯虚函数
您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
我们可以把基类中的虚函数 area() 改写如下:
1 2 3 4 5 6 7 8 9 10 11 12 | class Shape { protected : int width, height; public : Shape( int a=0, int b=0) { width = a; height = b; } // pure virtual function virtual int area() = 0; }; |
= 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数。
The word polymorphism means having many forms. Typically, polymorphism occurs when there is a hierarchy of classes and they are related by inheritance.
C++ polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that invokes the function.
Consider the following example where a base class has been derived by other two classes −
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a = 0, int b = 0){
width = a;
height = b;
}
int area() {
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape {
public:
Rectangle( int a = 0, int b = 0):Shape(a, b) { }
int area () {
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape {
public:
Triangle( int a = 0, int b = 0):Shape(a, b) { }
int area () {
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// Main function for the program
int main() {
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);