c++抽象类和纯虚函数
- 格式:docx
- 大小:14.03 KB
- 文档页数:4
虚函数原理虚函数是 C++ 中一个非常重要的特性,它为面向对象编程提供了很强的支持。
虚函数的实现原理是通过虚函数表实现的,本文将介绍虚函数的概念、使用方法以及实现原理。
一、虚函数概念虚函数是指在基类中使用 virtual 关键字声明的成员函数,它的作用是允许在子类中对该函数进行覆盖。
具体来说,虚函数允许在子类中定义一个与基类中同名的函数,当使用子类对象调用该函数时,程序会动态的选择调用子类中的函数。
虚函数的语法如下:```class Base {public:virtual void foo();};```虚函数可以被重写(覆盖),也可以被继承,但是不能被 static 和 friend 修饰。
二、虚函数的使用使用虚函数需要满足一下条件:1.虚函数必须在公有的类成员函数列表中声明,并在类声明的内部定义。
2.虚函数必须在基类和派生类中以相同的参数列表进行定义。
下面是一个使用虚函数的简单例子:class Square: public Shape {public:Square(double s) : side(s) {}double getArea() { return side * side; }Shape 是一个基类,Square 是它的一个派生类,Square 中重写了 getArea() 函数,计算正方形的面积。
虚函数的实现原理是通过虚函数表实现的。
虚函数表是一个指针数组,存储了每个类中的虚函数指针。
当对象被创建时,会在其内存空间中创建一个指向虚函数表的指针,这个指针通常称为虚函数表指针(vptr),虚函数的调用就是通过这个指针完成的。
每个含有虚函数的类都有一个独立的虚函数表,虚函数表智能在类的第一个对象中存储,它包含了该类中所有虚函数的地址。
在派生类中,虚函数表通常继承自它的直接基类,并在此基础上添加或修改虚函数的地址。
这样如果在派生类对象中调用虚函数时,程序会先获得对象的虚函数表指针,然后通过该指针找到对应的虚函数地址来执行函数。
C++基础class、struct、union详细⽬录1、类class2、结构体struct3、共⽤体union1、类class类是⾯向对象中的特性,在c中是没有类的概念。
通常⽤class来表⽰,cpp中有抽象类,但是没有接⼝这种说法,cpp⽀持多继承。
⼀个普通的类:class Fruit{private:public:Fruit();~Fruit();};Fruit::Fruit(){}Fruit::~Fruit(){}构造函数和析构函数:其中Fruit()表⽰构造函数,~Fruit()表⽰析构函数。
构造函数⽤来创建对象,设置初始化参数。
析构函数在对象销毁的时候执⾏。
修饰符:private:表⽰私有成员,外部不可访问,只有⾃⾝类和友元函数可以访问。
public:表⽰公共成员,外部可以访问。
protected:表⽰保护成员,保护成员和私有成员相似,但是⼦类可以访问保护成员。
类中的成员函数:我们在类中创建函数的时候,可以直接初始化,或者在类外部实现:class Fruit{private:int count;public:Fruit();~Fruit();void add(int i);//直接初始化int getCount(){return count;}};Fruit::Fruit(){cout << "create fruit" << endl;}Fruit::~Fruit(){cout <<"fruit deleted"<<endl;}//在类外部实现void Fruit::add(int i){count = count + i;}友元函数:友元函数虽然可以在类中定义,但是它不属于类的成员函数,必须在类外部实现。
它可以访问定义类中的private和protected成员。
友元类:友元类中的所有函数都是该类的友元。
C++基抽象类的构造析构(纯)虚函数⼀、析构函数可定义为⼀、析构函数可定义为纯虚函数纯虚函数,但也必须给出函数定义,但也必须给出函数定义 Effective C++ 条歀07: 为多态基类声明virtual 析构函数(Declare destructors virtual in polymorphic base classes ) 在某些类⾥声明纯虚析构函数很⽅便。
纯虚函数将产⽣抽象类——不能实例化的类(即不能创建此类型的对象)。
有些时候,你想使⼀个类成为抽象类,但刚好⼜没有任何纯虚函数。
怎么办?因为抽象类是准备被⽤做基类的,基类必须要有⼀个虚析构函数,纯虚函数会产⽣抽象类,所以⽅法很简单:在想要成为抽象类的类⾥声明⼀个纯虚析构函数。
1 //这⾥是⼀个例⼦:2 class awov {3 public :4 virtual ~awov() = 0; // 声明⼀个纯虚析构函数5 }; 这个类有⼀个纯虚函数,所以它是抽象的,⽽且它有⼀个虚析构函数,所以不会产⽣析构函数问题。
但这⾥还有⼀件事:必须提供纯虚析构函数的定义: awov::~awov() { ... } // 纯虚析构函数的定义 这个定义是必需的,因为虚析构函数⼯作的⽅式是:最底层的派⽣类的析构函数最先被调⽤,然后各个基类的析构函数被调⽤。
这就是说,即使是抽象类,编译器也要产⽣对~awov 的调⽤,所以要保证为它提供函数体。
如果不这么做,链接器就会检测出来,最后还是得回去把它添上。
⼆、? 关于C++为什么不⽀持虚拟构造函数,Bjarne 很早以前就在C++Style and Technique FAQ ⾥⾯做过回答 Avirtual call is a mechanism to get work done given partialinformation. In particular, "virtual" allows us to call afunction knowing only an interfaces and not the exact type of theobject. To create an object you need complete information.Inparticular, you need to know the exact type of what you want tocreate. Consequently, a "call to a constructor" cannot bevirtual. 含义⼤概是这样的:虚函数调⽤是在部分信息下完成⼯作的机制,允许我们只知道接⼝⽽不知道对象的确切类型。
纯虚函数和抽象类:含有纯虚函数的类是抽象类,不能生成对象,只能派生。
他派生的类的纯虚函数没有被改写,那么,它的派生类还是个抽象类。
定义纯虚函数就是为了让基类不可实例化化,因为实例化这样的抽象数据结构本身并没有意义.或者给出实现也没有意义一. 纯虚函数在许多情况下,在基类中不能给出有意义的虚函数定义,这时可以把它说明成纯虚函数,把它的定义留给派生类来做。
定义纯虚函数的一般形式为:class 类名{virtual 返回值类型函数名(参数表)= 0; // 后面的"= 0"是必须的,否则,就成虚函数了};纯虚函数是一个在基类中说明的虚函数,它在基类中没有定义,要求任何派生类都定义自己的版本。
纯虚函数为各派生类提供一个公共界面。
从基类继承来的纯虚函数,在派生类中仍是虚函数。
二. 抽象类1. 如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类(abstract class)。
抽象类中不仅包括纯虚函数,也可包括虚函数。
抽象类中的纯虚函数可能是在抽象类中定义的,也可能是从它的抽象基类中继承下来且重定义的。
2. 抽象类特点,即抽象类必须用作派生其他类的基类,而不能用于直接创建对象实例。
一个抽象类不可以用来创建对象,只能用来为派生类提供一个接口规范,派生类中必须重载基类中的纯虚函数,否则它仍将被看作一个抽象类。
3. 在effective c++上中提到,纯虚函数可以被实现(定义),但是,不能创建对象实例,这也体现了抽象类的概念。
三. 虚析构函数虚析构函数: 在析构函数前面加上关键字virtual进行说明,称该析构函数为虚析构函数。
虽然构造函数不能被声明为虚函数,但析构函数可以被声明为虚函数。
一般来说,如果一个类中定义了虚函数,析构函数也应该定义为虚析构函数。
例如:class B{virtual ~B(); //虚析构函数…};关于更多的精彩解释,请参考<< c++编程思想 >> 一书。
纯虚构造函数纯虚构造函数是指在抽象类中定义的没有实现的构造函数。
它的作用是强制子类在实现自己的构造函数时必须调用父类的构造函数,从而确保父类的成员变量被正确地初始化。
在C++中,纯虚构造函数的语法如下:```class A {public:virtual A() = 0;};```需要注意的是,纯虚构造函数不能被实例化,因此它的实现必须由子类来完成。
子类必须在自己的构造函数中调用父类的构造函数,否则编译器会报错。
纯虚构造函数的使用场景主要是在抽象类中。
抽象类是指不能被实例化的类,它的作用是为了让子类继承它的接口和实现。
抽象类中定义的纯虚函数和纯虚构造函数都没有实现,因此子类必须实现它们才能被实例化。
下面是一个简单的例子,演示了如何使用纯虚构造函数:```class Shape {public:virtual Shape() = 0;virtual double area() = 0;};class Circle : public Shape {public:Circle(double r) {radius = r;}double area() {return 3.14 * radius * radius;}private:double radius;};class Rectangle : public Shape {public:Rectangle(double w, double h) {width = w;height = h;}double area() {return width * height;}private:double width;double height;};int main() {Circle c(2.0);Rectangle r(3.0, 4.0);cout << "Circle area: " << c.area() << endl;cout << "Rectangle area: " << r.area() << endl; return 0;}```在上面的例子中,Shape是一个抽象类,它定义了纯虚构造函数和纯虚函数area()。
C选择题填空题一、填空题1、在类中必须声明成员函数的( ),成员函数的( )部分可以写在类外。
[答案]原型实现2、如果需要在被调函数运行期间,改变主调函数中实参变量的值,则函数的形参应该是( )类型或()类型。
[答案]引用指针3、()类只能作为基类使用,而不能声明它的对象。
[答案]抽象4、拷贝构造函数的形参必须是()[答案]本类对象的引用5、进行函数重载时,被重载的同名函数如果都没有用const修饰,则它们的形参()或()必须不同。
[答案]个数类型6、通过一个()对象只能调用它的常成员函数,不能调用其他成员函数。
[答案]常二、选择题1、编译时多态性使用什么获得?(A)A. 重载函数B. 继承C. 虚函数D. B和C2、假定MyClass为一个类,则该类的拷贝构造函数的声明语句为(C)。
A.MyClass(MyClass x)B.MyClass&(MyClass x)C.MyClass(MyClass &x)D.MyClass(MyClass *x)3、列带缺省值参数的函数说明中,正确的说明是(C)1. A. int Fun(int x=1, int y, int z=3);B. int Fun(int x, int y=2, int z);C. int Fun(int x, int y, int z=3);D. int Fun(int x=1, int y, int z);4、下列有关函数重载的叙述中,错误的是(A)A.函数重载就是用相同的函数名定义多个函数B.重载函数的参数列表必须不同C.函数的返回值类型不能用于对重载函数的区分D.const关键字可以用于对重载函数的区分5、在一个函数中,要求通过函数来实现一种不太复杂的功能,并且要求加快执行速度,选用(A)。
A. 内联函数B. 重载函数C. 递归调用D. 嵌套调用6、下列有关C++类的说法中,不正确的是(C)。
A. 类是一种用户自定义的数据类型B. 只有类中的成员函数或类的友元函数才能存取类中的私有成员C. 在类中,如果不做特别说明,所有成员的访问权限均为私有的D. 在类中,如果不做特别说明,所有成员的访问权限均为公用的7、Sample是一个类,执行下面语句后,执行Sample类的构造函数的次数是(D) Sample a[3], *p=new Sample;A.1B.2C.3D.48、关于常数据成员的说法,不正确的是(D)。
C++之普通成员函数、虚函数以及纯虚函数的区别与⽤法要点普通成员函数是静态编译的,没有运⾏时多态,只会根据指针或引⽤的“字⾯值”类对象,调⽤⾃⼰的普通函数;虚函数为了重载和多态的需要,在基类中定义的,即便定义为空;纯虚函数是在基类中声明的虚函数,它可以再基类中有定义,且派⽣类必须定义⾃⼰的实现⽅法。
假设我们有三个类Person、Teacher、Student它们之间的关系如下:类的关系图普通成员函数【Demo1】根据这个类图,我们有下⾯的代码实现#ifndef __OBJEDT_H__#define __OBJEDT_H__#include <string>#include <iostream>class Person{public:Person(const string& name, int age) : m_name(name), m_age(age){}void ShowInfo(){cout << "姓名:" << m_name << endl;cout << "年龄:" << m_age << endl;}protected:string m_name; //姓名int m_age; //年龄};class Teacher : public Person{public:Teacher(const string& name, int age, const string& title): Person(name, age), m_title(title){}void ShowInfo(){cout << "姓名:" << m_name << endl;cout << "年龄:" << m_age << endl;cout << "职称:" << m_title << endl;}private:string m_title; //职称};class Student : public Person{public:Student(const string& name, int age, int studyId): Person(name, age), m_studyId(studyId){}void ShowInfo(){cout << "姓名:" << m_name << endl;cout << "年龄:" << m_age << endl;cout << "学号:" << m_studyId << endl;}private:int m_studyId; //学号};#endif //__OBJEDT_H__测试代码:void test(){Person* pPerson = new Person("张三", 22);Teacher* pTeacher = new Teacher("李四", 35, "副教授");Student* pStudent = new Student("王五", 18, 20151653);pPerson->ShowInfo();cout << endl;pTeacher->ShowInfo();cout << endl;pStudent->ShowInfo();cout << endl;delete pPerson;delete pTeacher;delete pStudent;}结果:姓名:张三年龄:22姓名:李四年龄:35职称:副教授姓名:王五年龄:18学号:20151653说明:这⾥的ShowInfo就是⼀个普通的函数。
C++中的抽象类(Abstract Class)是一种特殊的类,它不能被实例化,只能被用作其他类的基类。
在本文中,我们将探讨C++中抽象类的用法以及如何在实际编程中应用抽象类。
1. 抽象类的定义在C++中,抽象类是指包含至少一个纯虚函数(Pure Virtual Function)的类。
纯虚函数是通过在函数声明的末尾加上`= 0`来定义的,它表示这个函数在基类中没有实现,而是由派生类去实现。
通过在类中定义纯虚函数,我们可以将抽象类作为接口类来使用,这样派生类就必须实现这些纯虚函数,从而实现了对派生类的约束。
2. 抽象类的用法在实际编程中,抽象类通常用来定义接口和创建框架,以便于派生类来实现具体的功能。
通过抽象类,我们可以将各个子类的共同特征提取出来,形成一个通用的接口,从而提高代码的复用性和可维护性。
3. 声明抽象类要声明一个抽象类,我们需要在类中定义至少一个纯虚函数。
例如:```cppclass AbstractClass {public:virtual void pureVirtualFunction() = 0;// Other members and functions};```在上面的代码中,pureVirtualFunction()是一个纯虚函数,这样AbstractClass就成了一个抽象类。
4. 派生类的实现当我们定义一个派生类来继承抽象类时,必须实现抽象类中的所有纯虚函数。
否则,编译器会报错,因为派生类没有完全实现抽象类的接口。
```cppclass ConcreteClass : public AbstractClass {public:void pureVirtualFunction() override {// 实现纯虚函数的具体逻辑}// Other members and functions};```在派生类中,我们通过override关键字来显式地标识出我们在实现一个抽象类中的纯虚函数。
c++ 基类纯虚函数C++是一种广泛使用的编程语言,同时也是面向对象编程语言。
在C++中,一个类可以从另一个类继承,这个类被称为基类,而继承的类被称为派生类。
基类中的纯虚函数是C++中非常重要的概念之一,它们在设计类的继承层次结构时非常有用。
纯虚函数是一种在基类中定义的虚函数,它没有任何实现代码,只是为了被继承类实现。
纯虚函数可以用一对`virtual`和`= 0`来声明,例如:```virtual void MyFunction() = 0;```这个声明告诉编译器MyFunction是一个虚函数,并且没有实现,只是一个接口,继承类必须对其进行实现。
纯虚函数在基类中起到了规范和约束作用,因为派生类必须实现这个函数才能实现自己的功能。
在许多情况下,基类中的纯虚函数是被设计为通用算法,由派生类提供特定的实现。
这种方法被称为“模板方法”模式。
在一个简单的图形库中,我们可以定义一个基类Shape,这个基类包含一个纯虚函数`Draw()`和派生类Rectangle和Circle。
Rectangle和Circle分别提供它们自己的特殊化实现,Draw()方法则会被调用以完成具体的实际操作。
在C++中,派生类中的实现方法可以通过覆盖和重载来完成。
覆盖是指派生类重新定义基类中的虚函数,以提供不同的实现方法。
重载是指派生类定义具有相同名称的函数,但它们具有不同的参数列表,这使得可以在相同的类中实现两个或更多的函数。
在实际开发中,如果我们定义了一个纯虚函数但没有提供实现,那么它将无法被实例化,因为它是一个抽象的函数。
通常情况下,如果我们忘记实现这个函数,可能会在编译时收到一个错误消息。
在设计一个类的继承时,纯虚函数是一种非常有用的技术。
它可以帮助我们将代码和数据聚集在一起,以便更好地组织和管理。
纯虚函数还可以使我们更迅速和简单地实现代码的重用和复用。
在C++中,基类中的纯虚函数是非常重要的。
它们可以帮助我们在类的继承层次结构中实现一些非常有用的功能,例如模板方法和多态。
C语言虚函数中的特定函数简介C语言是一种面向过程的编程语言,并不直接支持面向对象的概念,其中包括了“类”、“对象”、“继承”等概念。
然而,通过使用一些技巧和设计模式,我们可以在C语言中实现类似于面向对象的功能,其中一个重要的概念就是虚函数。
虚函数是一种特殊的函数,它可以在派生类中被重写,从而实现多态。
虚函数的定义、用途和工作方式是C语言中面向对象编程的重要部分,本文将详细介绍这些内容。
虚函数的定义在C语言中,虚函数的定义需要使用函数指针和结构体实现。
我们可以使用函数指针将一个函数地址赋值给一个结构体中的成员变量,从而形成一个具有特定功能的“方法”。
这样,我们就可以通过这个函数指针来调用结构体中的函数,实现类似于面向对象中对象的方法调用的功能。
下面是一个虚函数的定义示例:typedef struct {void (*function_ptr)(void);} VTable;void function1(void) {printf("This is function1\n");}void function2(void) {printf("This is function2\n");}VTable vtable = {.function_ptr = function1};在上述示例中,我们使用typedef定义了一个VTable结构体,其中有一个function_ptr成员变量,它是一个指向函数的指针。
我们定义了两个函数function1和function2,并分别赋值给了vtable中的function_ptr成员变量。
虚函数的用途虚函数的主要用途是实现多态,使不同类型的对象可以调用相同的接口名称,但执行不同的操作。
通过使用虚函数,我们可以在C语言中实现类似于面向对象的继承和多态的功能。
在面向对象的编程中,我们可以定义一个基类(或接口),然后派生出不同的子类,每个子类都可以重写基类的虚函数,以实现它们自己的特定行为。
C++中虚函数和纯虚函数的区别与总结⾸先:强调⼀个概念定义⼀个函数为虚函数,不代表函数为不被实现的函数。
定义他为虚函数是为了允许⽤基类的指针来调⽤⼦类的这个函数。
定义⼀个函数为纯虚函数,才代表函数没有被实现。
定义纯虚函数是为了实现⼀个接⼝,起到⼀个规范的作⽤,规范继承这个类的程序员必须实现这个函数。
1、简介假设我们有下⾯的类层次:class A{public:virtual void foo(){cout<<"A::foo() is called"<<endl;}};class B:public A{public:void foo(){cout<<"B::foo() is called"<<endl;}};int main(void){A *a = new B();a->foo(); // 在这⾥,a虽然是指向A的指针,但是被调⽤的函数(foo)却是B的!return 0;}这个例⼦是虚函数的⼀个典型应⽤,通过这个例⼦,也许你就对虚函数有了⼀些概念。
它虚就虚在所谓“推迟联编”或者“动态联编”上,⼀个类函数的调⽤并不是在编译时刻被确定的,⽽是在运⾏时刻被确定的。
由于编写代码的时候并不能确定被调⽤的是基类的函数还是哪个派⽣类的函数,所以被成为“虚”函数。
虚函数只能借助于指针或者引⽤来达到多态的效果。
C++纯虚函数⼀、定义 纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派⽣类都要定义⾃⼰的实现⽅法。
在基类中实现纯虚函数的⽅法是在函数原型后加“=0”virtual void funtion1()=0⼆、引⼊原因1. 为了⽅便使⽤多态特性,我们常常需要在基类中定义虚拟函数。
2. 在很多情况下,基类本⾝⽣成对象是不合情理的。
例如,动物作为⼀个基类可以派⽣出⽼虎、孔雀等⼦类,但动物本⾝⽣成对象明显不合常理。
选择题1.C++语言本身没有定义I/O操作,C++标准库iostream提供了基本的I/O类。
I/O 操作分别由两个类istream和( C )提供,由它们派生出一个类iostream,提供双向I/O操作。
A. fstreamB. iostreamC. ostreamD. cout2. 引入内联函数的主要目的是( B )。
A.缩短程序代码,少占用内存空间B.既可以保证程序的可读性,又能提高程序的运行效率C.占用内存空间少,执行速度快D.使程序的结构比较清晰3. 重载函数是( A )A. 以函数参数来区分,而不用函数的返回值来区分不同的函数B.以函数的返回值来区分,而不用函数参数来区分不同的函数C. 参数表完全相同而返回值类型不同的两个或多个同名函数D.参数表和返回值类型都必须是不同的两个或多个同名函数4. 在C++中,数据封装要解决的问题是( D )。
A. 数据的规范化B. 便于数据转换C. 避免数据丢失D. 防止不同模块之间数据的非法访问5. 下列特性中,( B )不是面向对象的程序设计的特征。
A.继承B.内联函数C.多态性D.封装6. 类B的对象必定是类A的对象,但是类A的对象却不属于类B,则类B和类A 之间的关系是( C)。
A. 组合关系B. 间接关系C. 继承关系D. 直接关系7. 下面对于友元函数描述正确的是( C)。
A. 友元函数的实现必须在类的内部定义B.友元函数是类的成员函数C. 友元函数破坏了类的封装性D.友元函数不能访问类的私有成员8. 以下关于析构函数的描述中( C )是错误的。
A.析构函数声明和定义中没有形参B.析构函数可以定义为内联函数。
C.析构函数可以重载D.析构函数可以定义为虚函数。
9. 下面叙述错误的是( A)。
A. 基类的protected成员在派生类中仍然是protected的B. 基类的protected成员在public派生类中仍然是protected的。
虚函数和虚基类的区别 C++虚函数,纯虚函数,抽象类以及虚基类的区别Part1.C++中的虚函数什么是虚函数:直观表达就是,如果⼀个函数的声明中有 virtual 关键字,那么这个函数就是虚函数。
虚函数的作⽤:虚函数的最⼤作⽤就是实现⾯向对象程序设计的⼀⼤特点,多态性,多态性表达的是⼀种动态的概念,是在函数调⽤期间,进⾏动态绑定,以达到什么样的对象就实现什么样的功能的效果。
虚函数的⼀般声明语法:virtual 函数类型函数名 (形参表)注意:虚函数的声明只能出现在类的定义中,不能出现在成员函数实现的时候虚函数⼀般不声明为内联函数,但是声明为内联函数也不会引起错误在运⾏过程中要实现多态的三个条件:类之间满⾜赋值兼容关系(也就是类之间有继承关系)要声明为虚函数调⽤虚函数时,要由成员函数或者是指针和引⽤来访问代码举例#include <iostream>using namespace std;class Base1 {public:public:virtual void play();};void Base1::play(){cout << "Base1::play()" << endl;}class Base2:public Base1{virtual void play();};void Base2::play() {cout << "Base2::play()" << endl;}class Derived :public Base2{virtual void play();};void Derived::play() {cout << "Derived:: play()" << endl;}void fun(Base1* ba) { //声明⼀个基类的指针ba->play();}int main(){Base1 ba1;Base2 ba2;Derived de;//分别⽤不同的对象指针来调⽤ fun 函数fun(&ba1);fun(&ba2);fun(&de);return 0;}这代码含义就充分体现了虚函数作为实现多态条件的原因,由于 Base1 是 Base2 和 Derived 的⽗类,所以,Base1 是可以兼容 Base2 和Derived 的,所以在 fun 函数这⾥是⽤的 Base1 的指针来作为形参,不同的是当我传⼊参数不同时,fun 函数执⾏的是不同的结果,这就体现了多态的效果,我需要那个类型的实例,他就执⾏那个实例对应的⽅法。
第一章面向对象第一节类与对象一、类1、类的概念类是对数据的一种抽象,所谓抽象就是将一些事物中不需要的数据剔除,而留下程序需要的。
类的声明声明一个类的语法:class 类名{private://私有的成员public://公有的成员};2、类的成员➢在类中可以声明任意类型的变量,即成员变量,表示这个类的属性。
比如说学生可以有一个整形变量表示id,字符数组表示姓名,浮点型变量表示成绩。
➢在类中可以声明函数,即成员函数,表示这个类的行为。
比如说学生可以有一个print 函数打印自己的成绩。
3、类的定义➢主要是对类中的成员函数进行定义➢定义类成员函数的语法:返回值类型类名::函数名(参数列表){函数体}4、定义类成员函数的注意事项当类成员函数定义在类的外部时,一定要在函数名前加上“类名::”,以引来表示该函数是类的成员函数。
在类的成员函数中可以自由的访问该类的其他成员属性和成员函数。
类成员函数的定义一般写在与头文件对应的.cpp文件中。
二、对象1、对象的概念➢从广义上讲,要在内存上一段有意义的区域就称之为对象。
➢在c++中,对象一般是指类在内存中装载的实例,具有相关的成员变量和成员函数。
类是抽象的概念,而对象是通过类实现的具体实例。
➢比如说,学生是类,学生小明是对象➢对象调用公有成员:可以通过运算符“:”或者“->”访问对象里的公有(public)成员➢this指针:⏹当通过对象调用成员函数传递参数时,会额外将本身的地址做为参数传递进入函数。
比如说我们实际调用成员函数如下:◆tom.introduce();⏹实际上编译器认为的代码是:◆tom.introduce(&tom);2、构造函数和析构函数➢由于对象一定会在内存中占用一段空间,所以一定会有其生命周期。
也就是说对象一定有申请内存空间和释放内存空间的步骤。
➢构造函数是当对象申请内存空间之后自动调用的函数。
➢析构函数是当对象的空间即将被销毁前自动调用的函数2..1、构造函数2.1.1 构造函数声明➢构造函数的声明需要在类中声明。
C P P复习题答案选择题1.C++语言本身没有定义I/O操作,C++标准库iostream提供了基本的I/O类。
I/O操作分别由两个类istream和( C )提供,由它们派生出一个类iostream,提供双向I/O操作。
A. fstreamB. iostreamC. ostreamD. cout2. 引入内联函数的主要目的是( B )。
A.缩短程序代码,少占用内存空间B.既可以保证程序的可读性,又能提高程序的运行效率C.占用内存空间少,执行速度快D.使程序的结构比较清晰3. 重载函数是( A )A. 以函数参数来区分,而不用函数的返回值来区分不同的函数B.以函数的返回值来区分,而不用函数参数来区分不同的函数C. 参数表完全相同而返回值类型不同的两个或多个同名函数D.参数表和返回值类型都必须是不同的两个或多个同名函数4. 在C++中,数据封装要解决的问题是( D )。
A. 数据的规范化B. 便于数据转换C. 避免数据丢失D. 防止不同模块之间数据的非法访问5. 下列特性中,( B )不是面向对象的程序设计的特征。
A.继承B.内联函数C.多态性D.封装6. 类B的对象必定是类A的对象,但是类A的对象却不属于类B,则类B和类A之间的关系是( C )。
A. 组合关系B. 间接关系C. 继承关系D.直接关系7. 下面对于友元函数描述正确的是( C )。
仅供学习与交流,如有侵权请联系网站删除谢谢2A. 友元函数的实现必须在类的内部定义B.友元函数是类的成员函数C. 友元函数破坏了类的封装性D.友元函数不能访问类的私有成员8. 以下关于析构函数的描述中( C )是错误的。
A.析构函数声明和定义中没有形参B.析构函数可以定义为内联函数。
C.析构函数可以重载D.析构函数可以定义为虚函数。
9. 下面叙述错误的是( A )。
A. 基类的protected成员在派生类中仍然是protected的B. 基类的protected成员在public派生类中仍然是protected的。
纯虚函数和抽象类纯虚函数和抽象类定义注意抽象类不能创建对象,但是可以定义⼀个指针注意抽象类不能有任何成员结构, 成员函数必须协成纯虚函数,virtual 返回值函数名(参数列表)=0注意 含有纯虚函数的类被称为抽象类。
抽象类只能作为派⽣类的基类,不能定义对象,但可以定义指针。
在派⽣类实现该纯虚函数后,定义抽象类对象的指针,并指向或引⽤⼦类对象。
1)在定义纯虚函数时,不能定义虚函数的实现部分;2)在没有重新定义这种纯虚函数之前,是不能调⽤这种函数的。
抽象类的唯⼀⽤途是为派⽣类提供基类,纯虚函数的作⽤是作为派⽣类中的成员函数的基础,并实现动态多态性。
继承于抽象类的派⽣类如果不能实现基类中所有的纯虚函数,那么这个派⽣类也就成了抽象类。
因为它继承了基类的抽象函数,只要含有纯虚函数的类就是抽象类。
纯虚函数已经在抽象类中定义了这个⽅法的声明,其它类中只能按照这个接⼝去实现。
抽象类实例计算图形⾯积#include <iostream>using namespace std;// 重点// ⾯向抽象类编程(⾯向⼀套预先定义好的接⼝编程)// 解耦合。
模块的划分class Figure // 抽象类{public:// 约定⼀个统⼀的界⾯(接⼝) 让⼦类使⽤,让⼦类必须去实现virtual void getArea() = 0; // 纯虚函数protected:private:};class Circle : public Figure {public:Circle(int a, int b) {this->a = a;this->b = b;}virtual void getArea() {cout << "圆的⾯积\t" << 3.14 * a * a << endl;}protected:private:int a;int b;};class Sanjiao : public Figure {public:Sanjiao(int a, int b) {this->a = a;this->b = b;}virtual void getArea() {cout << "三⾓形的⾯积\t" << a * b / 2 << endl;}protected:private:int a;int b;};class Squre : public Figure {public:Squre(int a, int b) {this->a = a;this->b = b;}virtual void getArea() {cout << "四边形的⾯积\t" << a * b << endl;}protected:private:int a;int b;};void PlayObj(Figure *base) {base->getArea(); // 会发⽣多态}int main() {// Figure f1; // 抽象类不能被实例化Figure *base = NULL;Circle c1(1, 2);Squre sq(1, 2);Sanjiao s1(2, 4);PlayObj(&c1);PlayObj(&s1);PlayObj(&sq);return0;}抽象类编程例⼦⼀计算程序员⼯资忘记了⼿动调⽤delete,让其调⽤析构函数⽐较好第⼆个有点错误, 不能说是抽象类#include <iostream>using namespace std;/*编写⼀个c++程序计算程序员⼯资(programer)1要求:能计算出初级程序员⼯资(junior_programer),中级程序员(mid_programer),⾼级程序员(Adv_progreamer) 2要求利⽤抽象类统⼀界⾯(⽅便程序拓展),⽐如新增计算架构师architect的⼯资*/// 程序员抽象类class programer{public:virtual void getSal() = 0; // 抽象类接⼝};// 初级程序员class junior_programer: public programer{public:junior_programer(char *name, char *job, int sal) // 浅拷贝{this->name = name;this->job = job;this->sal = sal;}virtual void getSal() // 接⼝类实现{cout << "name = " << name << "\tjob = " << job << "\tsal = " << sal << endl;}private:char *name;char *job;int sal;};// 中级程序员class mid_programer: public programer{public:mid_programer(char *name, char *job, int sal) // 浅拷贝{this->name = name;this->job = job;this->sal = sal;}virtual void getSal() // 接⼝类实现{cout << "name = " << name << "\tjob = " << job << "\tsal = " << sal << endl;}private:char *name;char *job;int sal;};// ⾼级程序员class Adv_programer: public programer{public:Adv_programer(char *name, char *job, int sal) // 浅拷贝{this->name = name;this->job = job;this->sal = sal;}virtual void getSal() // 接⼝类实现{cout << "name = " << name << "\tjob = " << job << "\tsal = " << sal << endl;}private:char *name;char *job;int sal;};// 后来增加的架构师类class architect: public programer{public:architect(char *name, char *job, int sal) // 浅拷贝{this->name = name;this->job = job;this->sal = sal;}virtual void getSal() // 接⼝类实现{cout << "name = " << name << "\tjob = " << job << "\tsal = " << sal << endl;}private:char *name;char *job;int sal;};// 计算函数,简单框架void jisuan(programer *base){base->getSal();}// 引⽤void jisuan(programer &base){base.getSal();}int main(){junior_programer junior("张三", "初级", 5000);mid_programer mid("李四", "中级", 10000);Adv_programer adv("王五", "⾼级", 15000);// 系统扩展增加代码架构师⼯资architect arc("康总", "架构师", 30000);jisuan(&junior);jisuan(&mid);jisuan(&adv);jisuan(&arc);cout << endl;// 引⽤类型auto &i = junior;auto &j = mid;auto &k = adv;auto &l = arc;jisuan(i);jisuan(j);jisuan(k);jisuan(l);return0;}别⼈的的⽅式//编写⼀个c++程序计算程序员⼯资(programer)//1要求:能计算出初级程序员⼯资(junior_programer),中级程序员(mid_programer),⾼级程序员(Adv_progreamer) //2要求利⽤抽象类统⼀界⾯(⽅便程序拓展),⽐如新增计算架构师architect的⼯资#include <iostream>#include <cstring>using namespace std;// 定义程序员类, 抽象类class Programer {virtual double getSal() = 0;};class Junior : Programer {public:Junior(char *name = NULL, char *job = NULL, double sal = 7000) {auto len = strlen(name);this->name = new char[len + 1];strcpy(this->name, name);len = strlen(job);this->job = new char[len + 1];strcpy(this->job, job);this->sal = sal;}virtual ~Junior() {delete[]name;delete[]job;sal = 0;name = NULL;job = NULL;cout << "j" << endl;}virtual double getSal() {cout << this->name << " : " << this->job << ": " << this->sal << endl; }public:char *name;char *job;double sal;};class Mid : public Junior {public:Mid(char *name = NULL, char *job = NULL, double sal = 10000): Junior(name, job, sal) {}virtual ~Mid() // 会默认调⽤⽗类的析构函数{cout << "m" << endl;}};// ⾼级的class Adv : public Junior {public:Adv(char *name = NULL, char *job = NULL, double sal = 10000): Junior(name, job, sal) {}virtual ~Adv() // 会默认调⽤⽗类的析构函数{cout << "Adv" << endl;}};void print(Junior &obj) {obj.getSal();}int main() {Junior j("张三", "初级", 5000);Mid m("李四", "中级", 10000);Adv a("王五", "⾼级", 15000);print(j);print(m);print(a);return0;}⾃⼰的另⼀种写法抽象类编程动物园类#if 0// main.cpp#define _CRT_SECURE_NO_WARNINGS #include <iostream>#include "Animal.h"#include "Dog.h"#include "Cat.h"#include "Dog.cpp"#include "Cat.cpp"#include "Animal.cpp"using namespace std;int main(void){letAnimalCry(new Dog);letAnimalCry(new Cat);#if 0Animal *dog = new Dog;letAnimalCry(dog);delete Dog;#endifreturn0;}#endif// -- Animal.h#if 0#pragma once#define _CRT_SECURE_NO_WARNINGS #include <iostream>using namespace std;class Animal{public://纯虚函数,让⼦类继承并且实现virtual void voice() = 0;Animal();virtual ~Animal();};//架构函数//让动物叫void letAnimalCry(Animal *animal);#endif// Animal.cpp#if 0#include "Animal.h"inlineAnimal::Animal(){cout << "animal().." << endl;}inlineAnimal::~Animal(){cout << "~Animal()..." << endl;}inlinevoid letAnimalCry(Animal *animal){animal->voice();// 需要⼿动调⽤delete 让其调⽤析构函数if (animal != NULL) {delete animal;}}#endif// Dog.h------------------------#if 0#pragma once#include "Animal.h"class Dog : public Animal{public:Dog();~Dog();virtual void voice();};#endif// Dog.cpp#if 0#include "Dog.h"inlineDog::Dog(){cout << "Dog().." << endl;}inlineDog::~Dog(){cout << "~Dog().." << endl;}inlinevoid Dog::voice(){cout << "狗开始哭了, 555" << endl;}#endif// Cat.h#if 0#pragma once#include "Animal.h"class Cat : public Animal{public:Cat();~Cat();virtual void voice();};#endif// Cat.cpp#if 0#include "Cat.h"inlineCat::Cat(){cout << "cat().." << endl;}inlineCat::~Cat(){cout << "~cat().." << endl;}inlinevoid Cat::voice(){cout << "⼩猫开始哭了,66666" << endl; }#endif动物园电脑类实例:#define _CRT_SECURE_NO_WARNINGS #include <iostream>using namespace std;//-------- 抽象层---------//抽象CPU类class CPU {public:// CPU();virtual void caculate() = 0;};//抽象的card类class Card {public:virtual void display() = 0;};//抽象的内存类class Memory {public:virtual void storage() = 0;};//架构类class Computer {public:Computer(CPU *cpu, Card *card, Memory *mem) { this->cpu = cpu;this->card = card;this->mem = mem;}void work() {this->cpu->caculate();this->card->display();this->mem->storage();}~Computer() {if (this->cpu != NULL) {cout << "~cpu" << endl;delete this->cpu;}if (this->card != NULL) {cout << "~card"<<endl;delete this->card;}if (this->mem != NULL) {cout << "~mem"<<endl;delete this->mem;}}private:CPU *cpu;Card *card;Memory *mem;};// --------------------------//-----------实现层----------//具体的IntelCPUclass IntelCPU : public CPU {public:virtual void caculate() {cout << "Intel CPU开始计算了" << endl;}};class IntelCard : public Card {public:virtual void display() {cout << "Intel Card开始显⽰了" << endl;}};class IntelMem : public Memory {public:virtual void storage() {cout << "Intel mem开始存储了" << endl;};class NvidiaCard : public Card {public:virtual void display() {cout << "Nvidia 显卡开始显⽰了" << endl;}};class KingstonMem : public Memory {public:virtual void storage() {cout << "KingstonMem 开始存储了" << endl;}};//--------------------------void test(){Computer *com1 = new Computer(new IntelCPU, new IntelCard, new IntelMem); com1->work();delete com1; // 如果定义⼀个指针不要忘记释放}//--------业务层-------------------int main() {//1 组装第⼀台intel系列的电脑#if 0CPU *intelCpu = new IntelCPU;Card *intelCard = new IntelCard;Memory *intelMem = new IntelMem;Computer *com1 = new Computer(intelCpu, intelCard, intelMem);com1->work();Card *nCard = new NvidiaCard;Memory* kMem = new KingstonMem;Computer *com2 = new Computer(intelCpu, nCard, kMem);com2->work();delete intelCpu;#endif// Computer *com1 = new Computer(new IntelCPU, new IntelCard, new IntelMem); // com1->work();// delete com1; // 这⾥不要忘记释放test();return0;}这个好好看看圆类#define _CRT_SECURE_NO_WARNINGS#include <iostream>using namespace std;//抽象的图形类class Shape{public://打印出图形的基本你属性virtual void show() = 0;//得到图形的⾯积virtual double getArea() = 0;virtual ~Shape() {}};//圆类class Circle :public Shapepublic:Circle(double r) {this->r = r;}//打印出图形的基本你属性virtual void show() {cout << "圆的半径是 " << r << endl;}//得到图形的⾯积virtual double getArea() {cout << "获取圆的⾯积" << endl;return this->r*this->r *3.14;}~Circle() {cout << "圆的析构函数。