丰城市建设局网站,网络服务器分为哪几种,广州网站建设推广公司,免费推广1. C 中的多态
多态#xff08;Polymorphism#xff09;是面向对象编程中的一个重要特性#xff0c;它允许使用相同的接口来表示不同的类型。由于派生类重写基类方法#xff0c;然后用基类引用指向派生类对象#xff0c;调用方法时候会进行动态绑定#xff0c;这就是多态…1. C 中的多态
多态Polymorphism是面向对象编程中的一个重要特性它允许使用相同的接口来表示不同的类型。由于派生类重写基类方法然后用基类引用指向派生类对象调用方法时候会进行动态绑定这就是多态。
多态分为静态多态和动态多态
1. 静态多态编译器在编译期间完成的编译器会根据实参类型来推断该调用哪个函数如果有对应的函数就调用没有则在编译时报错。
静态多态编译时多态
静态多态通过函数重载、运算符重载和模板实现。它在编译期间就决定了调用哪个函数。
函数重载示例
#include iostream using namespace std;
class Print { public: void display(int i) { cout Integer: i endl; } void display(double f) { cout Float: f endl; } };
int main() { Print obj; obj.display(10); // 调用int版本 obj.display(3.14); // 调用double版本 return 0; }
在这里display函数被重载不同的参数类型决定了在编译期哪个函数被调用。
2.动态多态其实要实现动态多态需要几个条件——即动态绑定条件 1. 虚函数。基类中必须有虚函数在派生类中必须重写虚函数。 2. 通过基类类型的指针或引用来调用虚函数。
动态多态运行时多态
动态多态是通过虚函数实现的在运行时决定调用哪个函数。使用基类指针或引用指向派生类对象可以根据实际的对象类型调用相应的派生类的函数。
动态多态示例
#include iostream
using namespace std;class Base {
public:virtual void show() { // 虚函数cout Base class show endl;}virtual ~Base() {} // 虚析构函数防止内存泄漏
};class Derived : public Base {
public:void show() override {cout Derived class show endl;}
};int main() {Base *bptr new Derived(); // 基类指针指向派生类对象bptr-show(); // 动态绑定调用 Derived 类的 show()delete bptr; // 防止内存泄漏return 0;
}基类指针Base* bptr实际指向了Derived对象运行时根据对象的实际类型调用了Derived类的show()方法。
总结
静态多态通过函数重载和模板实现在编译时决定调用哪个函数。动态多态通过虚函数实现依赖于运行时的动态绑定。 2. 为什么要虚析构为什么不能虚构造
虚析构的必要性
当通过基类指针删除派生类对象时如果基类析构函数不是虚函数将不会调用派生类的析构函数可能会导致派生类的资源没有被释放。
非虚析构的示例
#include iostream
using namespace std;class Base {
public:~Base() {cout Base destructor endl;}
};class Derived : public Base {
public:~Derived() {cout Derived destructor endl;}
};int main() {Base *bptr new Derived();delete bptr; // 只会调用Base的析构函数return 0;
}输出只会显示Base destructorDerived类的析构函数不会被调用从而导致资源泄漏。
虚析构的解决方案
#include iostream
using namespace std;class Base {
public:virtual ~Base() { // 虚析构函数cout Base destructor endl;}
};class Derived : public Base {
public:~Derived() {cout Derived destructor endl;}
};int main() {Base *bptr new Derived();delete bptr; // 调用Derived的析构函数return 0;
}输出会显示 Derived destructor
Base destructor表明派生类的析构函数得到了正确调用避免了资源泄漏。
为什么不能虚构造
构造函数用于初始化对象而在构造期间虚函数表还没有建立或准备好。构造函数依赖编译时的类型不可能在构造时进行动态绑定。而且构造函数负责创建对象的基类部分和派生类部分如果构造函数是虚的会产生语义上的混淆。虚析构将可能会被继承的父类的析构函数设置为虚函数可以保证当我们new一个子类然后使用基类指针指向该子类对象释放基类指针时可以释放掉子类的空间防止内存泄漏。如果基类的析构函数不是虚函数在特定情况下会导致派生类无法被析构。 1. 用派生类类型指针绑定派生类实例析构的时候不管基类析构函数是不是虚函数都会正常析构 2. 用基类类型指针绑定派生类实例析构的时候如果基类析构函数不是虚函数则只会析构基类不会析构派生类对象从而造成内存泄漏。为什么会出现这种现象呢个人认为析构的时候如果没有虚函数的动态绑定功能就只根据指针的类型来进行的而不是根据指针绑定的对象来进行所以只是调用了基类的析构函数如果基类的析构函数是虚函数则析构的时候就要根据指针绑定的对象来调用对应的析构函数了。 C默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针占用额外的内存。不能虚构造 1. 从存储空间角度虚函数对应一个vtale,这个表的地址是存储在对象的内存空间的。如果将构造函数设置为虚函数就需要到vtable中调用可是对象还没有实例化没有内存空间分配如何调用。 2. 从实现上看vtable在构造函数调用后才建立因而构造函数不可能成为虚函数。 3. 模板类是在什么时候实现的
模板类的定义和实现通常在头文件中一起写出它们在编译时实例化即编译器在遇到模板被使用时才根据实际的模板参数生成相应的代码。这也是为什么模板函数的定义通常需要与声明放在同一个文件里。
模板类的示例
#include iostream
using namespace std;templatetypename T
class Box {
private:T value;
public:Box(T val) : value(val) {}void display() {cout Value: value endl;}
};int main() {Boxint intBox(10); // 模板实例化为int类型Boxdouble doubleBox(5.5); // 模板实例化为double类型intBox.display(); // 输出Value: 10doubleBox.display(); // 输出Value: 5.5return 0;
}在编译时根据Boxint和Boxdouble的使用情况编译器生成具体的Boxint和Boxdouble代码。
总结
模板在编译时进行实例化。模板代码通常写在头文件中防止出现链接错误。 4. 构造函数为什么不能被声明为虚函数
构造函数的主要任务是创建并初始化对象而在构造函数中虚函数表vtable尚未建立。
构造函数依赖编译时类型即构造对象的类型在编译时就已经确定不需要依赖虚函数机制。另一方面虚函数机制依赖于虚表vtable而虚表是在构造函数执行完毕后才建立的。如果构造函数是虚的虚表还没有准备好因此无法进行动态绑定。 5. 什么是常函数有什么作用
常函数const成员函数是在函数声明时在函数名之后添加const关键字表明该函数不会修改对象的任何成员变量。
常函数的示例
class MyClass { private: int value; public: MyClass(int v) : value(v) {} int getValue() const { // 常函数 return value; } void setValue(int v) { value v; // 非常函数可以修改成员变量 } };
作用
常函数的主要作用是保证对象的状态不会在函数中被改变尤其是在使用const对象时。如果一个对象是常量const则只能调用常函数无法调用非常函数。
int main() {const MyClass obj(10);// obj.setValue(20); // 错误不能调用非常函数cout obj.getValue() endl; // 正确常函数可以被const对象调用return 0;
}6. 什么是虚继承解决什么问题如何实现
虚继承的定义
虚继承用于解决菱形继承问题。当多个派生类通过不同路径继承同一个基类时会导致基类的成员在最终派生类中存在多份拷贝造成数据冗余或歧义。虚继承可以确保最终派生类中只有一份基类的拷贝。
菱形继承问题的示例
#include iostream
using namespace std;class Base {
public:int value;
};class Derived1 : public Base { // 非虚继承
};class Derived2 : public Base { // 非虚继承
};class FinalDerived : public Derived1, public Derived2 {
public:void setValue(int v) {// value v; // 错误编译器无法确定要修改 Derived1 还是 Derived2 的 value}
};在这个例子中FinalDerived类中有两个Base类的拷贝一个来自Derived1另一个来自Derived2。因此value存在二义性。
虚继承解决菱形继承问题
#include iostream
using namespace std;class Base {
public:int value;
};class Derived1 : virtual public Base { // 虚继承
};class Derived2 : virtual public Base { // 虚继承
};class FinalDerived : public Derived1, public Derived2 {
public:void setValue(int v) {value v; // 不会有二义性因为Base类只有一份拷贝}
};通过虚继承FinalDerived类中只有一份Base类的拷贝避免了菱形继承问题。 7. 虚函数和纯虚函数以及实现原理
虚函数
虚函数是使用virtual关键字声明的函数允许派生类覆盖基类中的实现。虚函数支持运行时多态通过动态绑定调用派生类的函数。
纯虚函数
纯虚函数是一种特殊的虚函数它在基类中没有定义必须在派生类中实现。语法将函数声明中的 0表示为纯虚函数。
虚函数与纯虚函数示例
#include iostream using namespace std;
class Base { public: virtual void show() { // 虚函数 cout Base class show endl; } virtual void print() 0; // 纯虚函数 };
class Derived : public Base { public: void show() override { cout Derived class show endl; } void print() override { cout Derived class print endl; } };
int main() { Base *ptr new Derived(); ptr-show(); // 调用Derived类的show ptr-print(); // 调用Derived类的print delete ptr; return 0; }
Base类中的print()函数是纯虚函数派生类Derived必须提供实现。
实现原理
虚函数通过**虚函数表vtable**实现。每个包含虚函数的类都会维护一个虚函数表表中存储了指向实际函数的指针。在运行时编译器会根据对象的实际类型查找虚表调用相应的函数。 8. 纯虚函数能实例化吗为什么派生类要实现吗为什么
纯虚函数的类不能实例化
包含纯虚函数的类称为抽象类。由于纯虚函数没有实现所以抽象类不能被实例化否则在调用纯虚函数时会无从执行。示例 class AbstractClass { public: virtual void func() 0; // 纯虚函数 }; // AbstractClass obj; // 错误抽象类不能实例化
派生类必须实现纯虚函数
如果派生类不实现基类中的纯虚函数派生类也会变成抽象类无法实例化。因此派生类必须实现所有继承的纯虚函数。 9.虚函数和纯虚函数区别。
1. 虚函数Virtual Function
定义
虚函数是基类中使用 virtual 关键字声明的成员函数。它允许派生类提供自己的实现支持运行时多态。
特点
具有函数体虚函数在基类中有完整的实现派生类可以选择是否覆盖它。可选的覆盖派生类可以重写虚函数也可以不重写。如果派生类不提供自己的实现调用该函数时会使用基类的版本。动态绑定虚函数通过虚表vtable实现动态绑定运行时根据实际对象的类型选择调用合适的函数版本。
示例
#include iostream
using namespace std;class Base {
public:virtual void show() { // 虚函数cout Base class show endl;}
};class Derived : public Base {
public:void show() override { // 重写虚函数cout Derived class show endl;}
};int main() {Base *b new Derived();b-show(); // 调用Derived类的showdelete b;return 0;
}这里Base类中定义了虚函数 show()Derived 类重写了该函数。在运行时通过基类指针调用的 show() 实际调用了 Derived 类的实现。
2. 纯虚函数Pure Virtual Function
定义
纯虚函数是没有实现的虚函数它在基类中声明但不提供函数体需要在派生类中实现。纯虚函数的声明使用 0 表示。
特点
没有函数体纯虚函数在基类中没有定义仅作为接口存在。抽象类包含纯虚函数的类是抽象类不能被实例化。如果一个类包含一个或多个纯虚函数它就无法创建对象除非派生类实现所有的纯虚函数。必须被重写所有派生类必须实现基类中的纯虚函数否则派生类也将成为抽象类无法实例化。
示例
#include iostream
using namespace std;class Base {
public:virtual void show() 0; // 纯虚函数
};class Derived : public Base {
public:void show() override { // 实现纯虚函数cout Derived class show endl;}
};int main() {Base *b new Derived();b-show(); // 调用Derived类的showdelete b;return 0;
}在此例中Base 类定义了一个纯虚函数 show()因此它是一个抽象类不能实例化。但 Derived 类实现了该纯虚函数因此可以创建 Derived 类的对象并通过基类指针调用重写的函数。
3. 虚函数与纯虚函数的区别
比较维度虚函数Virtual Function纯虚函数Pure Virtual Function函数体在基类中有实现通常派生类可以选择重写或者使用基类实现。在基类中没有函数体必须在派生类中实现。类的类型包含虚函数的类不是抽象类可以实例化。包含纯虚函数的类是抽象类不能实例化。派生类实现派生类可以重写虚函数但不是必须的。派生类必须实现纯虚函数除非它本身也是抽象类。多态性支持运行时多态基类指针或引用可以调用派生类的重写函数。也是运行时多态的一部分依赖派生类的实现来调用函数。使用目的提供一个可以被覆盖的默认实现但允许派生类根据需要进行重写。定义一个接口强制派生类提供自己的实现。实现原理通过虚函数表vtable实现动态绑定。也是通过虚函数表vtable实现动态绑定但必须有实现函数。
4. 使用场景和设计目的 虚函数当你希望在基类中提供函数的默认行为但允许派生类根据需要选择重写时可以使用虚函数。这种设计允许部分多态行为而不强制所有派生类都必须实现该函数。 纯虚函数当你希望基类只定义接口并且要求所有派生类都必须提供自己的实现时使用纯虚函数。这种设计常见于抽象基类中它们定义了派生类的共同行为接口但不提供具体实现。 5. 总结
虚函数是可以在基类中提供实现的函数派生类可以选择重写它们也可以直接使用基类的实现。纯虚函数定义了一个必须由派生类实现的接口它没有实现派生类必须提供它的定义否则它们也将是抽象类无法实例化。