文档库 最新最全的文档下载
当前位置:文档库 › 静态函数 静态数据成员与静态成员函数 为什么虚函数必须是非静态成员函数 构造函数能为static吗

静态函数 静态数据成员与静态成员函数 为什么虚函数必须是非静态成员函数 构造函数能为static吗

静态函数 静态数据成员与静态成员函数 为什么虚函数必须是非静态成员函数 构造函数能为static吗
静态函数 静态数据成员与静态成员函数 为什么虚函数必须是非静态成员函数 构造函数能为static吗

静态函数静态数据成员与静态成员函数为什么虚函数必须是非静态成员函数构造函数能为static吗?

2009-07-05 14:27

静态函数

用static声明的函数是静态函数。静态函数可以分为全局静态函数和类的静态成员函数。

Static关键字

在类中,用static声明的成员变量为静态成员变量,它为该类的公用变量,在第一次使用时被初始化,对于该类的所有对象来说,static成员变量只有一份。用static声明的方法是静态方法,在调用该方法时,不会将对象的引用传递给它,所以在static方法中不可访问非static的成员。

静态方法不再是针对于某个对象调用,所以不能访问非静态成员。

可以通过对象引用或类名(不需要实例化)访问静态成员

C++类静态数据成员与类静态成员函数

函数调用的结果不会访问或者修改任何对象(非static)数据成员,这样的成员声明为静态成员函数比较好。且如果static int func(....)不是出现在类中,则它不是一个静态成员函数,只是一个普通的全局函数,只不过由于static的限制,它只能在文件所在的编译单位内使用,不能在其它编译单位内使用。

静态成员函数的声明除了在类体的函数声明前加上关键字static,以及不能声明为const或者volatile之外,与非静态成员函数相同。出现在类体之外的函数定义不能制定关键字static。

静态成员函数没有this指针。

在没有讲述本章内容之前如果我们想要在一个范围内共享某一个数据,那么我们会设立全局对象,但面向对象的程序是由对象构成的,我们如何才能在类范围内共享数据呢?

这个问题便是本章的重点:声明为static的类成员或者成员函数便能在类的范围内共同享,我们把这样的成员称做静态成员和静态成员函数。

下面我们用几个实例来说明这个问题,类的成员需要保护,通常情况下为了不违背类的封装特性,我们是把类成员设置为protected(保护状态)的,但是我们为了简化代码,使要说明的问题更为直观,更容易理解,我们在此处都设置为public。

以下程序我们来做一个模拟访问的例子,在程序中,每建立一个对象我们设置的类静态成员变自动加一,代码如下:

#include

using namespace std;

class Internet

{

public:

Internet(char *name,char *address)

{

strcpy(Internet::name,name);

strcpy(Internet::address,address);

count++;

}

static void Internet::Sc()//静态成员函数

{

cout<

}

Internet &Rq();

public:

char name[20];

char address[20];

static int count;//这里如果写成static int count=0;就是错误的};

Internet& Internet::Rq()//返回引用的成员函数

{

return *this;

}

int Internet::count = 0;//静态成员的初始化

void vist()

{

Internet a1("中国软件开发实验室","https://www.wendangku.net/doc/717938198.html,");

Internet a2("中国软件开发实验室","https://www.wendangku.net/doc/717938198.html,");

}

void fn(Internet &s)

{

cout<

}

void main()

{

cout<

vist();

Internet::Sc();//静态成员函数的调用

Internet b("中国软件开发实验室","https://www.wendangku.net/doc/717938198.html,");

Internet::Sc();

fn(b);

cin.get();

}

上面代码我们用了几种常用的方式建立对象,当建立新对象并调用其构造函数的时候,静态成员cout便运行加1操作,静态成员的初始化应该在主函数调用之前,并且不能在类的声明中出现,通过运行过程的观察我们发现,静态成员count 的状态并不会随着一个新的对象的新建而重新定义,尽而我们了解到类的静态成员是属于类的而不是属于哪一个对象的,所以静态成员的使用应该是类名称加域区分符加成员名称的,在上面的代码中就是Internet::count,虽然我们仍然可

以使用对象名加点操作符号加成员名称的方式使用,但是不推荐的,静态类成员的特性就是属于类而不专属于某一个对象。

静态成员函数的特性类似于静态成员的使用,同样与对象无关,调用方法为类名称加域区分符加成员函数名称,在上面的代码中就是Internet::Sc();静态成员函数由于与对象无关系,所以在其中是不能对类的普通成员进行直接操作的。

如果上面的 static void Internet::Sc()修改成为:

static void Internet::Sc()//静态成员函数

{

cout<

cout<

}

静态成员函数与普通成员函数的差别就在于缺少this指针,没有这个this指针自然也就无从知道name是哪一个对象的成员了。

根据类静态成员的特性我们可以简单归纳出几点,静态成员的使用范围:

1.用来保存对象的个数。

2.作为一个标记,标记一些动作是否发生,比如:文件的打开状态,打印机的使用状态,等等。

3.存储链表的第一个或者最后一个成员的内存地址。

为了做一些必要的练习,深入的掌握静态对象的存在的意义,我们以前面的结构体的教程为基础,用类的方式描述一个线性链表,用于存储若干学生的姓名,代码如下:

#include

using namespace std;

class Student

{

public:

Student (char *name);

~Student();

public:

char name[30];

Student *next;

static Student *point;

};

Student::Student(char *name)

{

strcpy(Student::name,name);

this->next=point;

point=this;

}

Student::~Student ()//析构过程就是节点的脱离过程

{

cout<<"析构:"<

if(point==this)

{

point=this->next;

cin.get();

return;

}

for(Student *ps=point;ps;ps=ps->next)

{

if(ps->next==this)

{

cout<next<<""<next<

ps->next=next;//=next也可以写成this->next; cin.get();

return;

}

}

cin.get();

}

Student* Student::point=NULL;

void main()

{

Student *c = new Student("marry");

Student a("colin");

Student b("jamesji");

delete c;

Student *fp=Student::point;

while(fp!=NULL)

{

cout<name<

fp=fp->next;

}

cin.get();

}

从上面的代码来看,原来单纯结构化编程需要的一个链表进入全局指针在这里被类的静态成员指针所替代(类的静态成员完全可以替代全局变量),这个例子的理解重点主要是要注意观察类成员的析构顺序,通过对析构顺序的理解,使用析构函数来进行节点的脱链操作。

为什么虚函数必须是非静态成员函数

如果定义为虚函数,那么它就是动态绑定的,也就是在派生类中可以被覆盖的,这与静态成员函数的定义本身就是相矛盾的。

==

主要有两个作用:

1、管理静态数据成员;

2、提供类范围的功能,即不需要对象来实现的功能。

比如Symbian中的NewL/LC方法就是static的

==

使用static关键字声明的函数成员是静态的,静态成员函数同样也属于整个类,由同一个类的所有对象共同维护,为这些对象所共享。

作为成员函数,它的访问属性可以受到类的严格控制,对于公有的静态函数成员函数,可以通过类名或对象名来调用,但一般情况下建议用对象名来引用静态函数成员(真的吗?)。注意,一般的成员函数只能通过对象名来调用。

由于一个类的静态成员函数只有一个拷贝,因此它访问对象的数据何函数使受到了限制。静态成员函数可以直接访问该类的静态数据成员。而访问非静态数据成

员,必须通过参数传递方式得到对象名,然后通过对象名来访问。可以看到,通过静态函数成员访问非静态成员使相当麻烦的,一般的使用中,它主要用来访问全局变量或同一个类中的静态数据成员,特别是和后者一起使用,达到对同一个类中对象之间共享的数据进行维护的目的。

构造函数不可以定义为static,看了上面,应该可以理解原因。

注意,由于static不是函数类型中的一部分,所以在类定义之外定义静态成员函数时不使用static,在类中定义的静态成员函数是内联的。

一般来说,通过成员名限定比使用对象名访问静态成员要好。因为静态成员不是对象的成员。

静态成员可以被继承,这时,基类对象和派生类的对象共享该静态成员,除此之外,在类等级中对静态成员的其他特性(例如,静态成员在派生类中的访问权限,在派生类中重载成员函数等)的分析与一般成员类似。

静态成员函数不能被申明为虚函数,静态成员具有外部连接属性,static仅有的含义是使该成员为该类的所有对象共享。

类中的任何成员函数都可以访问静态成员,但静态成员函数只能通过对象名(或指向对象的指针)访问该对象的非静态成员,因为静态成员函数没有this 指针。

虚函数必须是非静态成员函数

构造函数不可以定义为static

虚基类构造函数调用顺序

class Base1 { public: Base1(void) { cout << "class Base1" << endl; } }; class Base2 { public: Base2(void) { cout << "class Base2" << endl; } }; class Level1:virtual public Base2,public Base1 { public: Level1(void) { cout << "class Level1" << endl; } }; class Level2:public Base2,virtual public Base1 { public: Level2(void) { cout << "class Level2" << endl;

} }; class Leaf:public Level1,virtual public Level2 { public: Leaf(void) { cout << "class Leaf" << endl; } }; int main(void) { Leaf obj; return 0; } 不看下面的分析,大家觉得输出结果应该是怎么样的?如果没有虚拟继承,也许能很快说出答案。 现在来分析一下在多继承和虚拟继承的情况下,构造函数的调用顺序是怎么样的。 编译器按照直接基类在声明中的顺序,来检查虚拟基类的出现情况。在我们的例子中,Level1首先被检查,然后是Level2。每个继承子

树按照深度优先的顺序被检查。即,查找从树根类开始,然后向下移动。如对子树Level1而言,先检查Base2,然后是Base1,再到Level1。但是在虚拟继承中,基类构造函数的查找顺序只是为了知道虚拟继承的情况而已,基类构造函数的调用顺序和查找顺序是不一样的,那应该遵循什么样的一个原则呢? 遵循两个原则,而且按顺序优先满足:1 先调用完所以基类,再调用子类;2 先调用虚拟基类,再调用非虚拟基类。 一旦调用了虚拟基类的构造函数,则非虚拟基类构造函数就按照声明的顺序被调用。 所以针对我们这个例子,因为声明类Leaf的顺序是先Level1后Level2,所以先看看Level1这棵子树吧。 由于Level1虚拟继承Base2,非虚拟继承Base1,所以应该先调用Base2,但是这之后不能接着调用Level1这棵子树的Base1,因为其他子树还有虚拟继承。 现在来看看Level2这棵子树吧,由于Level2虚拟继承Base1,非虚拟继承Base2,所以先调用Base1,后Base2。既然Level2的两个基类都调用了,并且Level2也是一个虚拟基类,所以现在应该调用Level2的构造函数了。 这样,Level2这棵子树的构造函数都调用完了,又回到Level1这棵

虚函数和纯虚函数

虚函数和纯虚函数 在面向对象的C++语言中,虚函数(virtual function)是一个非常重要的概念。因为它充分体现了面向对象思想中的继承和多态性这两大特性,在C++语言里应用极广。比如在微软的MFC类库中,你会发现很多函数都有virtual关键字,也就是说,它们都是虚函数。难怪有人甚至称虚函数是C++语言的精髓。 那么,什么是虚函数呢,我们先来看看微软的解释: 虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。 ——摘自MSDN 这个定义说得不是很明白。MSDN中还给出了一个例子,但是它的例子也并不能很好的说明问题。我们自己编写这样一个例子: #include "stdio.h" #include "conio.h" class Parent { public: char data[20]; void Function1(); virtual void Function2(); // 这里声明Function2是虚函数 }parent; void Parent::Function1() { printf("This is parent,function1\n"); } void Parent::Function2() { printf("This is parent,function2\n"); } class Child:public Parent { void Function1(); void Function2(); } child; void Child::Function1() { printf("This is child,function1\n");

C++虚函数与纯虚函数用法与区别

C++虚函数与纯虚函数用法与区别 1.C++虚函数与纯虚函数用法与区别,.虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class)。 2.虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class) 只有声明而没有定义。 3.虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式被调用。 4.虚函数和纯虚函数通常存在于抽象基类(abstract base class -ABC)之中,被继承的子类重载,目的是提供一个统一的接口。 5.虚函数的定义形式:virtual {method body} 纯虚函数的定义形式:virtual { } = 0; 在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定(run-time bind),而且被两者修饰的函数生命周期(life recycle)也不一样。 6. 如果一个类中含有纯虚函数,那么任何试图对该类进行实例化的语句都将导致错误的产生,因为抽象基类(ABC)是不能被直接调用的。必须被子类继承重载以后,根据要求调用其子类的方法。 //father class class Virtualbase { public: virtual void Demon()= 0; //prue virtual function virtual void Base() {cout<<"this is farther class"<}; }

构造函数-析构函数的调用顺序

C++继承中构造函数、析构函数调用顺序及虚函数的动态绑定 昨天面试被问到这些,惭愧的很,居然搞混了,悔恨了一把。决定要彻底搞清楚。也算是有所收获。 首先说说构造函数,大家都知道构造函数里就可以调用成员变量,而继承中子类是把基类的成员变成自己的成员,那么也就是说子类在构造函数里就可以调用基类的成员了,这就说明创建子类的时候必须先调用基类的构造函数,只有这样子类才能在构造函数里使用基类的成员,所以是创建子类时先调用基类的构造函数然后再调用自己的构造函数。通俗点说,你要用某些物品,但这些物品你没办法自己生产,自然就要等别人生产出来,你才能拿来用。 接着就是析构函数了,上面说到子类是将基类的成员变成自己的成员,那么基类就会只存在子类中直到子类调用析构函数后。做个假设:假如在基类的析构函数调用比子类的先,这样会发生什么事呢?类成员终止了,而类本身却还在,但是在类存在的情况下,类成员就应该还存在的,这不就产生矛盾了吗?所以子类是调用自身的析构函数再调用基类的析构函数。 现在到了虚函数了,virtual主要作用是在多态方面,而C++的多态最主要的是类的动态绑定,动态绑定则是指将子类的指针或引用转换成基类对象,基类对象就可以动态判断调用哪个子类成员函数。这就说明在没有子类指针或引用转换为基类对象的话,virtual没有存在意义(纯虚函数除外),也就是有没有virtual都是调用其自身的成员函数。通过这些分析,对于virtu al就有了眉目了。当子类指针或引用转换为基类时,若基类中有用virtual定义的函数,被子类重写后,此基类对象就会根据子类调用子类中的重写后的函数,而不是基类中的函数;反之,若是基类中没有用virtual定义,则不管基类被赋值的是哪个子类的值,调用的都是基类的成员函数(当然指的是子类重载的基类函数,不然就算要调用子类特有的成员函数也会编译不过)。

纯虚函数

#include using namespace std; const double PI=3.1415926; class Shape { public: virtual double Area()=0; }; class Triangle:public Shape { private: double d,h; public: Triangle(double di,double gao) { d=di; h=gao; } double Area() { cout<<"三角形面积为:"; return d*h*1/2; } }; class Circle:public Shape { private: double r; public: Circle(double radius) { r=radius; } double Area() { cout<<"圆面积为:"; return PI*r*r; } }; class Ractangle:public Shape

{ private: double a,b; public: Ractangle(double chang,double kuang) { a=chang; b=kuang; } double Area() { cout<<"矩形面积为:"; return a*b; } }; void main() { Shape *p; double a,b; cout<<"请输入三角形底边和高:"; cin>>a>>b; Triangle t(a,b); p=&t; cout<Area()<>a; Circle c(a); p=&c; cout<Area()<>a>>b; Ractangle r(a,b); p=&r; cout<Area()<

C++中虚析构函数的作用

C++中虚析构函数的作用 我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。可是,为什么要这样做呢?下面用一个小例子来说明: 有下面的两个类: class ClxBase { public: ClxBase() {}; virtual ~ClxBase() {cout<<”aaa”<DoSomething(); delete pTest; 的输出结果是: Do something in class ClxDerived! Output from the destructor of class ClxDerived! aaa 这个很简单,非常好理解。 但是,如果把类ClxBase析构函数前的virtual去掉,那输出结果就是下面的样子了: Do something in class ClxDerived! aaa 也就是说,类ClxDerived的析构函数根本没有被调用!(注:肯定不会被调用,因为动态联

2009年C++上机考试试题(容易)

2009年C++上机考试试题 1.编写一个使用冒泡排序法进行排序操作的函数模板,并对整型数据和字符型数据进行排序。 2.编写一个程序计算球的表面积和体积。要求: (1)定义一个基类,至少含有一个数据成员半径,并设为保护成员; (2)定义基类的派生类球,含有求表面积和体积的成员函数和输出函数; (3)编写主函数,求球的表面积和体积。 (球的表面积计算公式:S=4πr^2, r为球半径。) (球的体积计算公式:V=(4/3) πr^3, r为球半径。) 3.编写一个程序计算圆柱的表面积和体积。要求: (1)定义一个基类,至少含有一个数据成员半径,并设为保护成员; (2)定义基类的派生类圆柱,含有求表面积和体积的成员函数和输出函数; (3)编写主函数,求圆柱的表面积和体积。 4.编写一个程序计算圆锥的体积。要求: (1)定义一个基类,至少含有一个数据成员半径,并设为保护成员; (2)定义基类的派生类圆锥,含有求体积的成员函数和输出函数; (3)编写主函数,求圆锥的体积。 (圆锥体积:3分之1 × 圆周率×r的平方× 圆锥的高) 5.设计并测试一个名为Ellipse的椭圆类,其数据成员为外切矩形的左上角与右下角两个点的坐标,计算并输出椭圆的面积。(椭圆面积公式S=圆周率×a×b(其中a,b分别是椭圆的长半轴,短半轴的长) 6.声明一个名为Ellipse的椭圆类,其公有的(public)数据成员为椭圆的外切矩形的左上角与右下角两个点的坐标;声明两个Ellipse类的对象,分别输入顶点坐标,计算并输出椭圆的面积;(椭圆面积公式S=圆周率×a×b(其中a,b分别是椭圆的长半轴,短半轴的长) 7.声明一个名为Ellipse的椭圆类,其私有的(private)数据成员为椭圆的外切矩形的左上角与右下角两个点的坐标,声明公有的(public)成员函数访问椭圆的外切矩形的顶点坐标;声明两个Ellipse类的对象,分别输入顶点坐标,计算并输出椭圆的面积;(椭圆面积公式S=圆周率×a×b(其中a,b分别是椭圆的长半轴,短半轴的长) 8.声明一个名为Ellipse的椭圆类,其私有的(private)数据成员为椭圆的外切矩形的左上角与右下角两个点的坐标,设计构造函数Ellipse(int,int,int,int)对椭圆的外切矩形的顶点坐标赋值,设计函数Area()计算椭圆的面积;声明两个Ellipse类的对象,计算并输出椭圆的面积。(椭圆面积公式S=圆周率×a×b(其中a,b分别是椭圆的长半轴,短半轴的长) 9.编写并测试一个函数,该函数实现求数列运算中从n个不同的数中取r个数的所有选择的个数。要求: (1)将main()函数放在一个文件中;

c++抽象类和纯虚函数

纯虚函数和抽象类: 含有纯虚函数的类是抽象类,不能生成对象,只能派生。他派生的类的纯虚函数没有被改写,那么,它的派生类还是个抽象类。 定义纯虚函数就是为了让基类不可实例化化,因为实例化这样的抽象数据结构本身并没有意义.或者给出实现也没有意义 一. 纯虚函数 在许多情况下,在基类中不能给出有意义的虚函数定义,这时可以把它说明成纯虚函数,把它的定义留给派生类来做。定义纯虚函数的一般形式为: class 类名{ virtual 返回值类型函数名(参数表)= 0; // 后面的"= 0"是必须的,否则,就成虚函数了}; 纯虚函数是一个在基类中说明的虚函数,它在基类中没有定义,要求任何派生类都定义自己的版本。纯虚函数为各派生类提供一个公共界面。 从基类继承来的纯虚函数,在派生类中仍是虚函数。 二. 抽象类 1. 如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类(abstract class)。 抽象类中不仅包括纯虚函数,也可包括虚函数。抽象类中的纯虚函数可能是在抽象类中定义的,也可能是从它的抽象基类中继承下来且重定义的。 2. 抽象类特点,即抽象类必须用作派生其他类的基类,而不能用于直接创建对象实例。 一个抽象类不可以用来创建对象,只能用来为派生类提供一个接口规范,派生类中必须重载基类中的纯虚函数,否则它仍将被看作一个抽象类。 3. 在effective c++上中提到,纯虚函数可以被实现(定义),但是,不能创建对象实例,这也体现了抽象类的概念。 三. 虚析构函数 虚析构函数: 在析构函数前面加上关键字virtual进行说明,称该析构函数为虚析构函数。虽然构造函数不能被声明为虚函数,但析构函数可以被声明为虚函数。 一般来说,如果一个类中定义了虚函数,析构函数也应该定义为虚析构函数。 例如: class B { virtual ~B(); //虚析构函数 … };

虚函数和纯虚函数的作用与区别

虚函数和纯虚函数的作用与区别 虚函数为了重载和多态的需要,在基类中是由定义的,即便定义是空,所以子类中可以重写也可以不写基类中的函数! 纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数! 虚函数 引入原因:为了方便使用多态特性,我们常常需要在基类中定义虚函数。 class Cman { public: virtual void Eat(){……}; void Move(); private: }; class CChild : public CMan { public: virtual void Eat(){……}; private: }; CMan m_man; CChild m_child; //这才是使用的精髓,如果不定义基类的指针去使用,没有太大的意义 CMan *p ; p = &m_man ; p->Eat(); //始终调用CMan的Eat成员函数,不会调用CChild 的 p = &m_child; p->Eat(); //如果子类实现(覆盖)了该方法,则始终调用CChild的Eat函数 //不会调用CMan 的Eat 方法;如果子类没有实现该函数,则调用CMan的Eat函数 p->Move(); //子类中没有该成员函数,所以调用的是基类中的 纯虚函数 引入原因: 1、同“虚函数”; 2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。 //纯虚函数就是基类只定义了函数体,没有实现过程定义方法如下 // virtual void Eat() = 0; 直接=0 不要在cpp中定义就可以了 //纯虚函数相当于接口,不能直接实例话,需要派生类来实现函数定义 //有的人可能在想,定义这些有什么用啊,我觉得很有用 //比如你想描述一些事物的属性给别人,而自己不想去实现,就可以定 //义为纯虚函数。说的再透彻一些。比如盖楼房,你是老板,你给建筑公司 //描述清楚你的楼房的特性,多少层,楼顶要有个花园什么的 //建筑公司就可以按照你的方法去实现了,如果你不说清楚这些,可能建筑

C++试题及答案 (五)

C++程序设计模拟试卷(五) 一、单项选择题(本大题共20小题,每小题1分,共20分)在每小题列出的四个备选项中 只有一个是符合题目要求的,请将其代码填写在题后的括号内。错选、多选或未选均无 分。 1. 静态成员函数没有() A. 返回值 B. this指针 C. 指针参数 D. 返回类型 答案:B 解析:静态成员函数是普通的函数前加入static,它具有函数的所有的特征:返回类型、 形参,所以使用静态成员函数,指针可以作为形参,也具有返回值。静态成员是类具有的 属性,不是对象的特征,而this表示的是隐藏的对象的指针,因此静态成员函数没有this 指针。静态成员函数当在类外定义时,要注意不能使用static关键字作为前缀。由于静态成员函数在类中只有一个拷贝(副本),因此它访问对象的成员时要受到一些限制:静态成员函数可以直接访问类中说明的静态成员,但不能直接访问类中说明的非静态成员;若要访问非静态成员时,必须通过参数传递的方式得到相应的对象,再通过对象来访问。 2. 在类的定义中,用于为对象分配内存空间,对类的数据成员进行初始化并执行其他内部管 理操作的函数是() A. 友元函数 B. 虚函数 C. 构造函数 D. 析构函数 答案:C 解析:定义构造函数作用就是初始化对象,而析构函数释放对象空间。虚函数用于完成多 态性,友元增加访问方便性。 3. 所有在函数中定义的变量,都是() A. 全局变量 B. 局部变量 C. 静态变量 D. 寄存器变量 答案:B 解析:变量存储类可分为两类:全局变量和局部变量。 (1)全局变量:在函数外部定义的变量称为全局变量,其作用域为:从定义变量的位置开始 到源程序结束。全局变量增加了函数之间数据联系的渠道,全局变量作用域内的函数,均可使用、修改该全局变量的值,但是使用全局变量降低了程序的可理解性,软件工程学提倡尽量避免使用全局变量。 (2)局部变量:在函数内部定义的变量称为局部变量,其作用域为:从定义变量的位置开始 到函数结束。局部变量包含自动变量(auto)静态变量(static)以及函数参数。 auto变量意味着变量的存储空间的分配与释放是自动进行的。说明符auto可以省略。函数中 的局部变量存放在栈空间。在函数开始运行时,局部变量被分配内存单元,函数结束时,局部变量释放内存单元。因此,任两个函数中的局部变量可以同名,因其占有不同的内存单元而不影响使用。这有利于实现软件开发的模块化。 static变量是定义在函数体内的变量,存放在静态存储区,不用栈空间存储,其值并不随存 储空间的释放而消失。 4. 假定AB为一个类,则执行“AB a(2), b[3],*p[4];”语句时调用该类构造函数的次数 为() A. 3 B. 4 C. 5 D. 9 答案:B 解析: a(2)调用1次带参数的构造函数,b[3]调用3次无参数的构造函数,指针没有给它 分配空间,没有调用构造函数。所以共调用构造函数的次数为4。 5. 如果表达式++a中的“++”是作为成员函数重载的运算符,若采用运算符函数调用格式,则可表示为() A. a.operator++(1) B. operator++(a) C. operator++(a,1) D. a.operator++() 答案:D 解析:运算符的重载,前缀先让变量变化。调用++a,等价为a.operator++(),注意无参 的形式。后缀的话a++,等价于a.operator(0),带形参,形参名可省。 6. 已知f1和f2是同一类的两个成员函数,但f1不能直接调用f2,这说明() A. f1和f2都是静态函数 B. f1不是静态函数,f2是静态函数 C. f1是静态函数,f2不是静态函数

001017[面向对象程序设计] 天津大学机考题库答案

面向对象程序设计复习题 一、单项选择题 1、下列对类的描述中,不符合C++语法的是( B )。 A.一个派生类至少有一个基类 B.一个派生类只能有一个基类 C.一个基类不可以有多个派生类 D.抽象类一定是另一个类的基类 2、用I/O流打开一个文件时,如果不指定文件存储方式,则该文件的格式是( B )。 A.二进制文件 B.文本文件 C.无格式 D.不确定 3、基类中的保护成员在其私有派生类中的访问属性是( A )。 A.私有 B.公有 C.保护 D.不确定 4、在面向对象程序设计中,基类和派生类用于实现的特性是( C )。 A.封装性 B.继承性 C.多态性 D.安全性 5、下列关于对象的描述中,正确的是( B )。 A.对象是一种数据类型 B.对象是类的实例 C.对象是对现实中同类事物的一种抽象描述 D.对象与现实中的一个物体相对应 6、下列关于类的描述中,正确的是( A )。 A.定义一个类就定义了一种新数据类型 B.类是一种特殊的变量 C.定义了一个类就会为类的数据成员分配存储空间 D.类中不能没有成员函数 7、C++中的虚函数可以实现的面向对象的基本特性是( D )。 A.封装性 B.继承性 C.抽象性 D.多态性 8、下列的整型常数中,符合C++语法的八进制常数是( A )。 A.007 B.301 C.098 D.0X12 9、下列常量中,正确的是( C )。 A.’ok!’ B.π C.34.78f D.1.8e-7.5 10、下列数组的定义形式中,正确的是( D )。 A.char s1[] 天津大学机考题库正确的答案是 B.char s2[3.5] 天津大学机考题库正确的答案是 C.char s3[3] 天津大学机考题库正确的答案是={‘a’,’x’,’d’,’\0’} D.char s4[20] 天津大学机考题库正确的答案是={“123”} 11、已知:int a[3] 天津大学机考题库正确的答案是[5] 天津大学机考题库 正确的答案是;则a[2] 天津大学机考题库正确的答案是表示的是( A )。 A.a[2] 天津大学机考题库正确的答案是[0] 天津大学机考题库正确的答案是 1/ 46

虚函数纯虚函数普通函数.docx

C++在继承中虚函数、纯虚函数、普通函数,三者的区别 1.虚函数(impure virtual) C++的帰函数主要作用是“运行时多态〃,父类屮提供虚函数的实现,为子类提供默认的函数实现。 子类可以重写父类的虚函数实现子类的特殊化。 如下就是一个父类中的虚函数: class A {public: virtual void out2(string s) { cout?"A(out2):"?s?endl; } 2.纯虚函数(pure virtual) C++中包含纯虚函数的类,被称为是"抽彖类〃。抽彖类不能使用newtU对彖,只有实现了这个纯虚函数的子类才能new出对象。 C++中的纯虚函数更像是〃只提供申明,没有实现〃,是对子类的约束,是“接口继承〃。 C++中的纯虚函数也是一种“运行时多态〃。 如下而的类包含纯虚函数,就是“抽彖类〃: class A {public: virtual void outl(string s)=0; virtual void out2(string s) { cout?"A(out2):"?s?endl; } }; 百 3.普通函数(no-virtual) 普通函数是静态编译的,没有运行时多态,只会根据指针或引用的“字面值〃类对象,调用自己的普通函数。 普通函数是父类为子类提供的“强制实现〃。 因此,在继承关系屮,子类不应该重写父类的普通函数,因为函数的调用至于类对彖的字面值有关。 4.程序综合实例 心 #inelude using namespace std; class A {public: virtual void outl()=0; ///由子类实现virtual ~A(){};

C++箴言:多态基类中将析构函数声明为虚拟

C++箴言:多态基类中将析构函数声明为虚拟 有很多方法可以跟踪时间的轨迹,所以有必要建立一个 TimeKeeper 基类,并为不同的计时方法建立派生类: class TimeKeeper { public: TimeKeeper(); ~TimeKeeper(); ... }; class AtomicClock: public TimeKeeper { ... }; class WaterClock: public TimeKeeper { ... }; class WristWatch: public TimeKeeper { ... }; 很多客户只是想简单地取得时间而不关心如何计算的细节,所以一个 factory 函数--返回一个指向新建派生类对象的基类指针的函数--被用来返回一个指向计时对象的指针:TimeKeeper* getTimeKeeper(); // returns a pointer to a dynamic- // ally allocated object of a class // derived from TimeKeeper 按照 factory 函数的惯例,getTimeKeeper 返回的对象是建立在堆上的,所以为了避免泄漏内存和其他资源,最重要的就是要让每一个返回的对象都可以被完全删除。 TimeKeeper *ptk = getTimeKeeper(); // get dynamically allocated object // from TimeKeeper hierarchy ... // use it delete ptk; // release it to avoid resource leak 现在我们精力集中于上面的代码中一个更基本的缺陷:即使客户做对了每一件事,也无法预知程序将如何运转。 问题在于 getTimeKeeper 返回一个指向派生类对象的指针(比如 AtomicClock),那个对象通过一个基类指针(也就是一个 TimeKeeper* 指针)被删除,而且这个基类

抽象基类和纯虚函数

抽象基类和纯虚函数 抽象类和具体类 包含纯虚函数的类不能实例化对象,是抽象类 如果抽象类的派生类实现了所有积累中的纯虚函数,则不再是抽象类 抽象类存在的意义是作为其他类的基类,也较抽象基类 构造函数的执行顺序:从上至下 析构函数的执行顺序:从下至上 创建对象时要执行正确的构造函数 撤销对象时要执行正确的析构函数 问题:动态对象的创建和撤销 虚析构函数 动态对象的创建 动态创建的对象没有问题 New classname(···); 动态对象的撤销 Delete 基类指针; 如果基类指针指向的是派生类的对象呢? 析构函数可以声明为虚函数 Delete 基类指针; 程序会根据积累指针指向的对象的类型确定要调用的析构函数 如果基类的析构函数为虚函数,则所派生的类的析构函数都是虚函数 如果要操作具有继承关系的类的动态对象,最好使用虚析构函数 文件和流——支持大量数据的处理:输入,存储 对文件执行的操作只要求我们掌握对几个函数的操作就行 如果说你不懂对文件的操作和处理,你永远也无法选好编程,你的程序永远也写不好,操作系统能够把外设和文件统一管理。 文件可以保存程序的运行结果 文件使程序处理大量的数据成为可能 大型系统的运行需要文件支持 C++将文件看成有序的字节流 文件被打开后,操作系统为该文件的建立的一个缓冲区,或称为一个字节序列,即流 普通文件 二进制文件 文本文件 输入输出设备:键盘,显示器,打印机等 标准输入流(用指针stdin操作) 标准输出流(用指针stdout操作)

C++采用相同的方式操作普通文件和I/O设备 文件的操作 格式化输入输出(文本) 块输入输出(二进制) 文件操作过程 1.建立并打开文件 2.操作文件:读,写 3.关闭文件 打开文件或建立一个新文件 FILE *fopen(const char *filename,const char *mode); filename——路劲及文件名 mode——打开方式 关闭文件 Int fclose(FILE *stream); Stream——要关闭的文件 读写文件——格式化操作(文本文件) Int fscanf(File *stream,·······); Int fprintf(File *stream,·······); 读写文件——快读写方式(二进制文件) size_t fwrite(const void*buffer,size_t size,size_t count,File *stream); size_t fread(const void*buffer,size_t size,size_t count,File *stream);

c++多态性与虚函数习题答案

多态性与虚函数 1.概念填空题 1.1 C++支持两种多态性,分别是编译时和运行时。 1.2在编译时就确定的函数调用称为静态联编,它通过使用函数重载,模板等实现。 1.3在运行时才确定的函数调用称为动态联编,它通过虚函数来实现。 1.4虚函数的声明方法是在函数原型前加上关键字virtual。在基类中含有虚函数,在派生类中的函数没有显式写出virtual关键字,系统依据以下规则判断派生类的这个函数是否是虚函数:该函数是否和基类的虚函数同名;是否与基类的虚函数参数个数相同、类型;是否与基类的虚函数相同返回类型。如果满足上述3个条件,派生类的函数就是虚函数。并且该函数覆盖基类的虚函数。 1.5 纯虚函数是一种特别的虚函数,它没有函数的函数体部分,也没有为函数的功能提供实现的代码,它的实现版本必须由派生类给出,因此纯虚函数不能是友元函数。拥有纯虚函数的类就是抽象类类,这种类不能实例化。如果纯虚函数没有被重载,则派生类将继承此纯虚函数,即该派生类也是抽象。 3.选择题 3.1在C++中,要实现动态联编,必须使用(D)调用虚函数。 A.类名 B.派生类指针 C.对象名 D.基类指针 3.2下列函数中,不能说明为虚函数的是(C)。 A.私有成员函数 B.公有成员函数 C.构造函数 D.析构函数 3.3在派生类中,重载一个虚函数时,要求函数名、参数的个数、参数的类型、参数的顺序和函数的返回值(A)。 A.相同 B.不同 C.相容 D.部分相同 3.4当一个类的某个函数被说明为virtual时,该函数在该类的所有派生类中(A)。 A.都是虚函数 B.只有被重新说明时才是虚函数 C.只有被重新说明为virtual时才是虚函数 D.都不是虚函数 3.5(C)是一个在基类中说明的虚函数,它在该基类中没有定义,但要求任何派生类都必须定义自己的版本。 A.虚析构函数B.虚构造函数 C.纯虚函数D.静态成员函数 3.6 以下基类中的成员函数,哪个表示纯虚函数(C)。 A.virtual void vf(int);B.void vf(int)=0; C.virtual void vf( )=0;D.virtual void vf(int){ } 3.7下列描述中,(D)是抽象类的特性。 A.可以说明虚函数 B.可以进行构造函数重载 C.可以定义友元函数 D.不能定义其对象 3.8类B是类A的公有派生类,类A和类B中都定义了虚函数func( ),p是一个指向类A对象的指针,则p->A::func( )将(A)。

C++复习题

一、单项选择题(本大题共20小题,每小题1分,共20分) 1. 静态成员函数没有() A. 返回值 B. this指针 C. 指针参数 D. 返回类型 2. 在类的定义中,用于为对象分配内存空间,对类的数据成员进行初始化并执行其他内部管理操作的函数是() A. 友元函数 B. 虚函数 C. 构造函数 D. 析构函数 3. 所有在函数中定义的变量,都是() A. 全局变量 B. 局部变量 C. 静态变量 D. 寄存器变量 4. 假定AB为一个类,则执行“AB a(2), b[3],*p[4];”语句时调用该类构造函数的次数为() A. 3 B. 4 C. 5 D. 9 5. 如果表达式++a中的“++”是作为成员函数重载的运算符,若采用运算符函数调用格式,则可表示为() A. a.operator++(1) B. operator++(a) C. operator++(a,1) D. a.operator++() 6. 已知f1和f2是同一类的两个成员函数,但f1不能直接调用f2,这说明()

A. f1和f2都是静态函数 B. f1不是静态函数,f2是静态函数 C. f1是静态函数,f2不是静态函数 D. f1和f2都不是静态函数 7. 一个函数功能不太复杂,但要求被频繁调用,则应把它定义为() A. 内联函数 B. 重载函数 C. 递归函数 D. 嵌套函数 8. 解决定义二义性问题的方法有() A. 只能使用作用域分辨运算符 B. 使用作用域分辨运算符或成员名限定 C. 使用作用域分辨运算符或虚基类 D. 使用成员名限定或赋值兼容规则 9. 在main函数中可以用p.a的形式访问派生类对象p的基类成员a,偶中a是() A. 私有继承的公有成员 B. 公有继承的私有成员 C. 公有继承皀保护成员 D. 公有廧承的公有成员 10. 在C++中不返回任何????数应该说明为() A. int B. char C. void D. double 11. 若Sample类中的一个成员函数说明如下: void set(Sample& a),则Sample& a的含义是() A. 指向类Sample的名为a的指针

C++期末考试复习重点、易错知识点整理

C++重点、易错知识点整理 第一章 1、泛型程序设计是指向程序中数据类型中加入类型参数的一种能力,也称为参数化的类型 或参数多态性。 2、c++程序开发通常要经过5个阶段,包括编辑、预处理、编译、连接、运行与调试。 3、编译过程分为词法分析、语法分析、代码生成这3个步骤。 4、使用名字空间std的方法有3种: 1、利用using namespace使用名字空间;使用方法如下: 2、用域分辨符::为对象分别指定名字空间;例如: 3、用using与域分辨符指定名字空间;例如: 5、c++中常用操作符: 第二章 1、c++的数据类型:

2、在定义变量的同时赋初值还有另外一种方法,就是在变量后面将初值放在括号中,格式 如下: 3、常变量定意格式: 或 ※在定义常变量时,一定要赋初值,且在程序中间不能更新其值。 4、常量和非左值表达式是没有内存地址的。 5、在逻辑表达式求值中注意短路求值。 6、运算符优先级的规律: (1)运算符的优先级按单目、双目、三目、赋值依次降低; (2)算术、移位、关系、按位、逻辑运算的优先级依次降低。 7、标准c++提供了新式的强制类型转换运算,格式如下: ※static_cast用于一般表达式的类型转换; ※reinterpret_cast用于非标准的指针数据类型转换,如将void*转换成char*; ※const_cast将const表达式转换成非常量类型,常用于将限制const成员函数的const 定义解除; ※dynamic_cast用于进行对象指针的类型转换。 第三章 第四章 1、内联函数的定义必须出现在对该函数的调用之前。 2、递归函数不能定义为内联函数。 3、说明一个内联函数只是请求而不是命令编译器对它进行扩展。 带有默认形参值的函数: 1、若函数具有多个形参,则默认形参值必须自右向左连续的定义,并且在一个默认形参值 的右边不能有未指定默认值的参数。 2、在调用一个函数时,若果省去了某个实参,则直到最右端的实参都要省去。 3、默认形参值的说明必须出现在函数调用之前。若函数原型中已给出了形参的默认值,则 在函数定义中不得重复制定,即使所指定的默认值完全相同也不行。 4、在同一个作用域内,一旦定义了默认形参值,就不能在定义它。 5、如果几个函数说明出现在不同的作用域内,则允许对它们提供不同的默认形参值。 6、在函数的原型给出了形参的默认值时,形参名可以省略。 第五章 1、相同类型的指针类型才可以想减;两个指针是不可以相加的。 2、一个void类型的地址赋值给非void类型的指针变量,要使用类型强制转换。 3、要初始化多重指针,要从第一层开始,逐步向高层进行。

2、定义一个Shape类含一个求面积的纯虚函数,由Shape类派生圆类、矩形类和三角形类求面积函数和求周长函数

2、定义一个基类Shap,包含一个求面积的纯虚函数,由Shap类派生圆类、矩形类和三角形类,定义各自的数据成员、构造函数、求面积函数和求周长函数。编写主函数,定义各类对象及基类指针,通过基类指针调用求面积和周长函数,计算各对象的面积和周长。 #include #include class Shape {public: virtual double mianji() const=0;}; class Yuan:public Shape {public: Yuan(double r):radius(r){} double mianji() const {cout<<"圆的面积为:"<<3.14159*radius*radius<

虚函数的作用

条款14: 确定基类有虚析构函数 有时,一个类想跟踪它有多少个对象存在。一个简单的方法是创建一个静态类成员来统计对象的个数。这个成员被初始化为0,在构造函数里加1,析构函数里减1。(条款m26里说明了如何把这种方法封装起来以便很容易地添加到任何类中,“my article on counting objects”提供了对这个技术的另外一些改进) 设想在一个军事应用程序里,有一个表示敌人目标的类: class enemytarget { public: enemytarget() { ++numtargets; } enemytarget(const enemytarget&) { ++numtargets; } ~enemytarget() { --numtargets; } static size_t numberoftargets() { return numtargets; } virtual bool destroy(); // 摧毁enemytarget对象后 // 返回成功 private: static size_t numtargets; // 对象计数器 }; // 类的静态成员要在类外定义; // 缺省初始化为0 size_t enemytarget::numtargets; 这个类不会为你赢得一份政府防御合同,它离国防部的要求相差太远了,但它足以满足我们这儿说明问题的需要。 敌人的坦克是一种特殊的敌人目标,所以会很自然地想到将它抽象为一个以公有继承方式从enemytarget派生出来的类(参见条款35及m33)。因为不但要关心敌人目标的总数,也要关心敌人坦克的总数,所以和基类一样,在派生类里也采用了上面提到的同样的技巧: class enemytank: public enemytarget { public: enemytank() { ++numtanks; } enemytank(const enemytank& rhs) : enemytarget(rhs) { ++numtanks; }

相关文档