当前位置:文档之家› C++构造函数详解及显式调用构造函数

C++构造函数详解及显式调用构造函数

C++构造函数详解及显式调用构造函数
C++构造函数详解及显式调用构造函数

C++构造函数详解及显式调用构造函数

c++类的构造函数详解

一、构造函数是干什么的

class Counter

{

public:

// 类Counter的构造函数

// 特点:以类名作为函数名,无返回类型

Counter()

{

m_value = 0;

}

private:

// 数据成员

int m_value;

}

该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数->由构造函数完成成员的初始化工作

eg: Counter c1;

编译系统为对象c1的每个数据成员(m_value)分配内存空间,并调用构造函数Counter( )自动地初始化对象c1的m_value值设置为0

故:

构造函数的作用:初始化对象的数据成员。

二、构造函数的种类

class Complex

{

private :

double m_real;

double m_imag;

public:

// 无参数构造函数

// 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做

// 只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来

Complex(void)

{

m_real = 0.0;

m_imag = 0.0;

}

// 一般构造函数(也称重载构造函数)

// 一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者

类型不同(基于c++的重载函数原理)

// 例如:你还可以写一个Complex( int num)的构造函数出来

// 创建对象时根据传入的参数不同调用不同的构造函数

Complex(double real, double imag)

{

m_real = real;

m_imag = imag;

}

// 复制构造函数(也称为拷贝构造函数)

// 复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中

// 若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询有关“浅拷贝” 、“深拷贝”的文章论述

Complex(const Complex & c)

{

// 将对象c中的数据成员值复制过来

m_real = c.m_real;

m_imag = c.m_imag;

}

// 类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象,

//需要注意的一点是,这个其实就是一般的构造函数,但是对于出现这种单参数的构造函数,C++会默认将参数对应的类型转换为该类类型,有时候这种隐私的转换是我们所不想要的,所以需要使用explicit来限制这种转换。

// 例如:下面将根据一个double类型的对象创建了一个Complex对象

Complex(double r)

{

m_real = r;

m_imag = 0.0;

}

// 等号运算符重载(也叫赋值构造函数)

// 注意,这个类似复制构造函数,将=右边的本类对象的值复制给等号左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建

// 若没有显示的写=运算符重载,则系统也会创建一个默认的=运算符重载,只做一些基本的拷贝工作

Complex &operator=( const Complex &rhs )

{

// 首先检测等号右边的是否就是左边的对象本身,若是本对象本身,则直接返回

if ( this == &rhs )

{

return *this;

}

// 复制等号右边的成员到左边的对象中

this->m_real = rhs.m_real;

this->m_imag = rhs.m_imag;

// 把等号左边的对象再次传出

// 目的是为了支持连等eg: a=b=c 系统首先运行b=c

// 然后运行a= ( b=c的返回值,这里应该是复制c值后的b对象)

return *this;

}

};

下面使用上面定义的类对象来说明各个构造函数的用法:

int main()

{

// 调用了无参构造函数,数据成员初值被赋为0.0

Complex c1,c2;

// 调用一般构造函数,数据成员初值被赋为指定值

Complex c3(1.0,2.5);

// 也可以使用下面的形式

Complex c3 = Complex(1.0,2.5);

// 把c3的数据成员的值赋值给c1

// 由于c1已经事先被创建,故此处不会调用任何构造函数

// 只会调用= 号运算符重载函数

c1 = c3;

// 调用类型转换构造函数

// 系统首先调用类型转换构造函数,将5.2创建为一个本类的临时对象,然后调用等号运算符重载,将该临时对象赋值给c1

c2 = 5.2;

// 调用拷贝构造函数( 有下面两种调用方式)

Complex c5(c2);

Complex c4 = c2; // 注意和= 运算符重载区分,这里等号左边的对象不是事先已经创建,故需要调用拷贝构造函数,参数为c2

//这一点特别重要,这儿是初始化,不是赋值。其实这儿就涉及了C++中的两种初始化的方式:复制初始化和赋值初始化。其中c5采用的是复制初始化,而c4采用的是赋值初始化,这两种方式都是要调用拷贝构造函数的。

区别:

初始化:被初始化的对象正在被创建

赋值:被赋值的对象已经存在

}

三、思考与测验

1. 仔细观察复制构造函数

Complex(const Complex & c)

{

// 将对象c中的数据成员值复制过来

m_real = c.m_real;

m_img = c.m_img;

}

为什么函数中可以直接访问对象c的私有成员?

答:(网上)因为拷贝构造函数是放在本身这个类里的,而类中的函数可以访问这个类的对象的所有成员,当然包括私有成员了。

2. 挑战题,了解引用与传值的区别

Complex test1(const Complex& c)

{

return c;

}

Complex test2(const Complex c)

{

return c;

}

Complex test3()

{

static Complex c(1.0,5.0);

return c;

}

Complex& test4()

{

static Complex c(1.0,5.0);

return c;

}

void main()

{

Complex a,b;

// 下面函数执行过程中各会调用几次构造函数,调用的是什么构造函数?

test1(a);

test2(a);

b = test3();

b = test4();

test2(1.2);

// 下面这条语句会出错吗?

test1(1.2); //test1( Complex(1.2 )) 呢?

}

答:

为了便于看构造函数的调用效果,我将类重新改一下,添加一些输出信息,代码如下:

View Code

下面是程序运行结果:

#include

using namespace std;

class Complex

{

private :

double m_real;

double m_imag;

int id;

static int counter;

public:

// 无参数构造函数

Complex(void)

{

m_real = 0.0;

m_imag = 0.0;

id=(++counter);

cout<<"Complex(void):id="<

}

// 一般构造函数(也称重载构造函数)

Complex(double real, double imag)

{

m_real = real;

m_imag = imag;

id=(++counter);

cout<<"Complex(double,double):id="<

}

// 复制构造函数(也称为拷贝构造函数)

Complex(const Complex & c)

{

// 将对象c中的数据成员值复制过来

m_real = c.m_real;

m_imag = c.m_imag;

id=(++counter);

cout<<"Complex(const Complex&):id="<

m_pName = new char[strlen(pN) + 1];

//在堆中开辟一个内存块存放pN所指的字符串

if(m_pName != NULL)

{

//如果m_pName不是空指针,则把形参指针pN所指的字符串复制给它

strcpy(m_pName ,pN);

}

}

// 系统创建的默认复制构造函数,只做位模式拷贝

Person(Person & p)

{

//使两个字符串指针指向同一地址位置

m_pName = p.m_pName;

}

~Person( )

{

delete m_pName;

}

private :

char * m_pName;

};

void main( )

{

Person man("lujun");

Person woman(man);

// 结果导致man 和woman 的指针都指向了同一个地址

// 函数结束析构时

// 同一个地址被delete两次

}

// 下面自己设计复制构造函数,实现“深拷贝”,即不让指针指向同一地址,而是重新申请一块内存给新的对象的指针数据成员

Person(Person & chs);

{

// 用运算符new为新对象的指针数据成员分配空间

m_pName=new char[strlen(p.m_pName)+ 1];

if(m_pName)

{

// 复制内容

strcpy(m_pName ,chs.m_pName);

}

// 则新创建的对象的m_pName与原对象chs的m_pName不再指向同一地址了

}

参考地址:https://www.doczj.com/doc/4315696509.html,/823160/194307下面讨论一个重要问题是:构造函数的显式调用大家看看下面这段代码的输出结果是什么?这段代码有问题么?#include

class CTest

{

public:

CTest()

{

m_a = 1;

}

CTest(int b)

{

m_b = b;

CTest();

}

~CTest()

{}

void show

{

std::cout << m_a << std::endl;

std::cout << m_b << std::endl;

}

private:

int m_a;

int m_b;

};

void main()

{

CTest myTest(2);

myTest.show();

}

-----------------------------------------------------------

【分析】

-----------------------------------------------------------

输出结果中,m_a是一个不确定的值,因为没有被赋初值,m_b 为2

注意下面这段代码

CTest(int b)

{

m_b = b;

CTest();

}

在调用CTest()函数时,实际上是创建了一个匿名的临时CTest类对象,CTest()中赋值m_a = 1 也是对该匿名对象赋值,故我们定义的myTest的m_a其实没有被赋值。说白了,其实构造函数并不像普通函数那样进行一段处理,而是创建了一个对象,并且对该对象赋初值,所以显式调用构造函数无法实现给私有成员赋值的目的。

这个例子告诉我们以后代码中千万不要出现使用一个构造函数显式调用另外一个构造函数,这样会出现不确定性。其实一些初始化的代码可以写在一个单独的init函数中,然后每一个构造函数都调用一下该初始化函数就行了。

在此,顺便再提出另外一个问题以供思考:

CTest *p = NULL;

void func()

{

p = new CTest();

}

代码右边显示调用CTest(),是否依然会产生一个匿名的临时对象a,然后将该匿名的临时对象a的地址赋给指针p? 如果是这样的话,出了func函数后,临时对象a是否会被析构? 那指针p不成为了野指针了?你能解释这个问题么?

答:我实验的结果是不会产生临时对象a,直接将产生的对象指针赋给了p

参考:https://www.doczj.com/doc/4315696509.html,/823160/294573

虚基类构造函数调用顺序

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主要作用是在多态方面,而C++的多态最主要的是类的动态绑定,动态绑定则是指将子类的指针或引用转换成基类对象,基类对象就可以动态判断调用哪个子类成员函数。这就说明在没有子类指针或引用转换为基类对象的话,virtual没有存在意义(纯虚函数除外),也就是有没有virtual都是调用其自身的成员函数。通过这些分析,对于virtu al就有了眉目了。当子类指针或引用转换为基类时,若基类中有用virtual定义的函数,被子类重写后,此基类对象就会根据子类调用子类中的重写后的函数,而不是基类中的函数;反之,若是基类中没有用virtual定义,则不管基类被赋值的是哪个子类的值,调用的都是基类的成员函数(当然指的是子类重载的基类函数,不然就算要调用子类特有的成员函数也会编译不过)。

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++试题及答案 (五)

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

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* 指针)被删除,而且这个基类

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、要初始化多重指针,要从第一层开始,逐步向高层进行。

C++复习题

C++作业题(8) 一.选择填空 (1) 定义重载函数的下列条件中,(C )是错误的。 A. 要求参数个数不同 B. 要求参数类型不同 C. 要求函数返回值类型不同 D. 要求在参数个数相同时,参数类型的顺序不同 (2) 关于下列虚函数的描述中,( C)是正确的。 A. 虚函数是一个static存储类的成员函数 B. 虚函数是一个非成员函数 C. 基类中说明了虚函数后,派生类中可不必将对应的函数说明为虚函数 D. 派生类的虚函数与基类的虚函数应具有不同的类型或个数 (3) 关于纯虚函数和抽象类的描述中,(C )是错误的。 A. 纯虚数是一种特殊的虚函数,它没有具体实现 B. 抽象类中一定具有一个或多个纯虚函数 C. 抽象类的派生类中一定不会再有纯虚函数 D. 抽象类一般作为基类使用,使纯虚函数的实现由其派生类给出 (4) 以下一种类中,( A)不能建立对象。 A. 抽象类 B. 派生类 C. 虚基类 D. 基类 (5)下列函数中不能重载的是( C )。 A)成员函数 B)非成员函数 C)析构函数 D)构造函数 (6)下列描述中,抽象类的特征有( D )。 A)可以说明虚函数 B)可以构造函数重载 C)可以定义友员函数 D)不能说明其对象(7)下列不属于动态联编实现的条件有( D )。 A)要有说明的虚函数。 B)调用虚函数的操作是指向对象的指针或者对象引用:或者是由成员函数调用虚函数。C)子类型关系的确立。 D)在构造函数中调用虚函数。 (8)派生类中对基类的虚函数进行替换时,派生类中说明的虚函数与基类中的被替换的虚

函数之间不要求满足的是( C )。 A)与基类的虚函数具有相同的参数个数。 B)其参数的类型与基类的虚函数的对应参数类型相同。 C)基类必须定义纯虚函数。 D)其返回值或者与基类的虚函数相同,或者都返回指针或引用,并且派生类虚函数所返回的指针或引用的基类型是基类中被替换的虚函数所返回的指针或引用的基类的子类型。(9)下列关于抽象类说法正确的是:( B ) A)抽象类处于继承类层次结构的较下层。 B)抽象类刻画了一组子类的操作通用接口。C)抽象类可以作为类直接使用。 D)抽象类可以直接定义对象。 (10)下列关于虚析构函数说法不正确的是( B )。 A)在析构函数前加上关键字virtual,就说明了虚析构函数。 B)如果一个基类的析构函数说明为虚析构函数,则它的派生类中的析构函数须用virtual 关键字说明后才是虚析构函数。 C)说明虚析构函数的目的在于使用delete删除一个对象时,能保证析构函数被正确地执行。D)设置虚函数后,可以采用动态联编的方式选择析构函数。 (11)编译时多态性通过使用( B )获得。 A)继承 B)虚函数 C)重载函数 D)析构函数 (12)可以使用( A )来阻止基类的成员函数调用派生类中的虚函数。 A)成员名限定 B)指针 C)引用 D)关键字virtual (13)抽象类应该含有( D )。 A)至多一个虚函数 B)至多一个虚函数是纯虚函数 C)至少一个虚函数 D)至少一个虚函数是纯虚函数 (14)一个抽象类可以说明为( A )。 A)指向抽象类对象的指针 B)类成员数据 C)抽象类的对象 D)数组元素(15)对于抽象类的使用需要注意的地方,下列不正确的说法是:( C ) A)抽象类只能用作其它类的基类,不能建立抽象类对象。 B)抽象类不能用作参数类型,函数返回类型或显式转换的类型。

2014级计算机C++(下)模拟试卷答案2

成贤学院计算机专业第二学期 C++程序设计期末模拟考试卷(100分钟) 姓名学号班级成绩 一.选择题(20分) 二.下列有关类和对象的说法中,正确的是__________。 A.类与对象没有区别B.要为类和对象分配存储空间 C.对象是类的实例,为对象分配存储空间而不为类分配存储空间 D.类是对象的实例,为类分配存储空间而不为对象分配存储空间 三.虚函数_________。 A.可实现静态多态 B.可实现动态多态 C.不能实现多态 D.既可实现静态多态,又可实现动态多态 四.重载运算“+”,实现a+b运算,则_____________。 A. a必须为对象,b可为整数或实数 B. a和b必须为对象 C. b必须为对象,a可为整数或实数 D. a和b均可为整数或实数 五.下列叙述中,不正确的是:。 A. 一个类可以不定义构造函数 B. 一个类可以不定义析构函数 C. 类的析构函数可以重载 D. 类的构造函数可以重载 六.类MyClass已经定义,执行语句“MyClass ca[3],*p[2]”时,将调用___次构造函数。 A.2 B.3 C.4 D.5 七.下列叙述正确的是________。 A.重载不能改变运算符的结合性 B.重载可以改变运算符的优先级 C.所有的C++运算符都可以被重载 D.运算符重载用于定义新的运算符 7. 对于指针运算正确的写法是。 A.int *p;p++; B.int x=5;int *p;p=x; C.int x=5;int *p=&x; D.int x=5;int *p;p=&x;p++;8. 虚函数可以实现对象的多态性。若要实现动态的多态性,需要定义一个指针 变量,用于指向不同派生类的对象,调用指针所指的虚函数即可实现其功能。 该指针变量的类型是。 A、基类类型 B、派生类类型 C、整型 D、void类型 9. 当说明派生类的对象时,调用构造函数的顺序正确的是__________。 A.先调用基类的构造函数,再调用派生类的构造函数 B.先调用派生类的构造函数,再调用基类的构造函数 C.调用基类的构造函数和调用派生类的构造函数之间的顺序无法确定 D.调用基类的构造函数和调用派生类的构造函数是同时进行的 10. 已知:p是一个指向类A数据成员m的指针,A1是类A的一个对象。如果要给m赋值为5,正确的语句是。 A. A1.p=5; B. A1->p=5; C. A1.*p=5; D. *A1.p=5; 11.设有以下定义:

c++实验报告二(构造函数、析构函数)

太原工业学院计算机工程系

return 0; } (3)使用带默认参数值的构造函数,不给定实参时,默认边长为1;注意——这个版本也只需要一个构造函数即可。需要的测试函数是: int main() { Triangle Tri1; //定义边长为1 1 1的三角形类实例 Tri1.showMessage(); Triangle Tri2(1.5);//定义边长为1.5 1 1的三角形类实例 Tri2.showMessage(); Triangle Tri3(1.5,1.5);//定义边长为1.5 1.5 1的三角形类实例 Tri3.showMessage(); Triangle Tri4(7,8,9); //定义边长为7 8 9的三角形类实例 Tri4.showMessage(); return 0; } (4)在构造函数中使用参数初始化表对数据成员初始化。 这是一种更加提倡的写法。测试函数同(1)。 【项目2-游戏中的角色类】 (1)基于下面设计的游戏中角色类,补充完整需要的成员函数,使角色能一定的规则行动或改变状态。下面代码中包含的是最基本的要求,可以根据你的设计进行扩充。 class Role { public: ...... private: string name; int blood; bool life; }; int main() { Role mary; mary.setRole(“mary”,4);//设置角色的姓名为“mary”,血量为4 mary.show();//显示 mary.attack();//攻击别人,涨1个血 mary.eat(2);//吃东西,涨2个血 mary.beAttack();//被攻击,失1个血,血量为0死掉 mary.beAttack();//被攻击,失1个血,血量为0死掉 mary.show();//显示 return 0; } (2)设计构造函数,使角色对象在创建时被初始化;设计析构函数,当角色对象被撤消时,提示一句“XXX(名称)退出江湖…”。可以用下面的代码进行测试:int main() { Role mary(“Mary”,4); Role jack(“Jack”,0); mary.show(); jack.show(); return 0; }

C++程序设计第八章

第八章 多态性和虚函数 8.1多态性 静态联编所支持的多态性称为编译时的多态性。动态联编所支持的多态性称为运行时的多态性, 8.1.1静态联编中的赋值兼容性及名字支配规律 8.1.2 动态联编的多态性 如果让编译器动态联编,也就是在编译“point*P=&c;”语句时,只根据兼容性规则检查它的合理性,也就是检查是否符合“派生类对象的地址可以赋给基类的指针”的条件。至于“p->area()” 调用哪个函数,等程序运行到这里时再决定。 就要使类point的指针p指向派生类函数area的地址。假设使用关键字virtual声明point的类area 函数,将这种函数称为虚函数。 当编译系统编译含有虚函数的类时,将为它建立以个虚函数表,表中的每个元素都指向一个虚函数地址。编译器为类增加一个数据成员,这个数据成员是一个指向该虚函数表的指针,通常称为vptr。 虚函数的地址翻译取决于对象的内存地址。编译器为含有虚函数的类的对象建立一个入口地址,这个地址用来存放指向虚函数表的指针vptr,然后按照类中的虚函数声明次序,一一填入函数指针。 派生类能继承基类的虚函数表,而且只要是和类同名(参数也相同)的成员函数,无论是否使用virtual 声明,它们都自动成为虚函数。如果派生类没有改写基类的虚函数,则函数指针调用基类的虚函数。如果派生改写了基类的函数,编译器将重新为派生类的虚函数建立地址,函数指针会调用改写过的虚函数。 虚函数的调用规则是:根据当前对象,优先调用对象本身的虚成员函数。 8.2 虚函数 一旦基类定义了虚函数该基类的派生类中的同名函数也自动成为虚函数。 8.2.1 虚函数 函数只能是类中的一个成员函数,但不能使静态成员, 8.2.2 虚函数实现多态的条件 关键字virtual指示C++编译器对调用虚函数进行动态联编。这种多态性是程序运行到需要的语句处才动态确定的,所以称为运行时的多态。 (1)类之间的继承关系满足赋值兼容规则。 (2)改写同名虚函数 (3)根据赋值兼容性规则使用指针 满足钱联条件不一定产生动态编译,必须同时满足第3条才能保证实现动态编译。第3友分为两种情况:第1中是已经演示过的按赋值兼容性规则使用基类指针(或引用)访问虚函数;第2种是把指针(或引用)作为函数参数,即这个函数不一定是类的成员函数,可以是普通函数,而且可以重载。 8.2.3构造函数和析构函数调用虚函数 在构造函数和析构函数中调用虚函数采用静态联编, 在建立类C的对象c时,它所包含的基类对象在派生类中定义的成员建立之前被建立。在对象撤销时,该对象所包含的在派生类中定义的成员要先于基类对象之前撤销。 目前推存的C++标准不支持虚构函数。有于析构函数不允许有参数,因此一个类只能有一个虚析构函数。虚析构函数使用vitual说明。 Delete运算符合析构函数一起工作(new和构造函数一起工作),当使用delete删除一个对象时,delete 隐含着对析构函数的一次调用,如果析构函数为虚函数,则这个调用采用动态联编。 8.2.4纯虚函数与抽象 Virtual 函数类型函数名(参数列表)=0;

《面向对象程序设计》第1.7.9.11.12章在线测试

1章 第一题、单项选择题(每题1分,5道题共5分) 1、下面不属于C++语言的特点的是 A、C++支持数据封装 B、C++支持继承性 C、C++支持动态绑定 D、C++不允许破环封装 2、下面的步骤不属于C++程序开发步骤的是 A、编辑程序 B、编译程序 C、连接程序 D、程序分析 3、执行完下列语句后,a、b、c三个变量的值为多少?() a =30; b =a++; c =++a; A、a:32 ;b:30 ;c:32; B、a:30 ;b:31 ;c:32; C、a:32 ;b:31 ;c:30; D、a:31 ;b:32 ;c:30; 4、VC项目中可以添加的文件类型不可以是 A、.CPP B、.C C、.H D、.LIB 5、下列关于条件语句的描述中,错误的是 A、if语句中只有一个else子句 B、if语句中可以有多个else if子句 C、if 语句中if体内不能是开关语句 D、if语句的if体内可以是循环语句 答案:DDADC 第二题、多项选择题(每题2分,5道题共10分) 1、C++的输出可以使用BC A、scanf B、printf C、cout D、out 2、以下语句循环次数为无限次的是BCD A、for ( y=0,x=1;x>++y;x=I++) I=x; B、for(;;x++=I); C、while(1){x++;} D、for(I=10;;I--)sum+=I; 3、关于重载函数正确的说法是ABD

A、函数重载指在同一作用域内多个函数使用相同的函数名 B、重载函数的参数必须不同 C、重载函数的返回值类型必须不同 D、两个功能相似的操作可以重载函数得到 4、下面函数声明正确的是ABD A、void f1(int a,int b); B、void f2(int a,int c,int b=-1); C、void f3(int a ,int b =-1, int c); D、int f4(…); 5、C++程序中声明变量int *p1 , *p2 ,m=5, n;下面错误的语句是ABD A、p1=&m; p2=&p1; B、p1=&m; p2=&n;*p1=*p2 C、p1=&m;p2=p1; D、p1=&m;*p2=*p1; 第三题、判断题(每题1分,5道题共5分) 1、if ( x = 3 ) 和if (x ==3) 这两条语句的作用是一样的。 错误 2、对内联函数不能进行异常接口声明。 正确 3、重载的函数的函数名是相同的,但它们的参数的个数和数据类型不同。 正确 4、重载的函数的函数名是相同的,但它们的参数的个数、参数数据类型或返回值类型中至少应有一种是不同的。 错误 5、在for循环设置条件的第一个";"前,可以用“,”分隔不同的赋值表达式。 正确

C实验十一虚函数.docx

实验十一虚函数 一、实验目的 1)掌握虚函数的定义和使用 2)掌握抽彖类的定义和使用 3)掌握纯虚函数的定义 4)掌握虚析构函数、纯虚函数和抽象类的作用 二、实验原理 1.利用虚函数的作用:当编译器编译虚函数吋,编译系统将用动态连接的方式进行编译。即在编译时不确定该虚函数的版本,而是利用一种机制在运动过程屮根据其所指向的实例决定使用哪一个函数版本。 2.利用虚析构函数的原则:当将基类指针或引用new运算符指向派生类对象时, 为了在释放派牛类对象时能调用派牛类的析构函数,必须将基类的析构函数定义为虚函数。 3.抽彖类的作用:为它的所有派生类提供一个公共接口,纯虚函数是定义抽彖类的一种间接手段。 三、实验设备 实验室里调备的计算机、window xp,visual c++6.0 四、实验内容 4.1分析下面各题程序,按各题的要求进行实验 1)分析下面的程序,指出程序运行的结果: # include class CBase {public: virtual void fl() 〃将成员函数fl()声明为虚函数 {cout?n调用函数CBase::fl()!H?endl;} virtual void f2() 〃将成员函数f2()声明为虚函数 {cout?"调用函数CBase::f2()!"?endl;} void f3() //一般成员函数 {cout?n调用函数CBase::f3()!H?endl;} }; class CDerived:public CBase 〃公有继承CBase {void fl() {cout?n调用函数CDerived::fl()!M?endl;} void f2() {cout?n调用函数CDerived::f2()!M?endl;} void f3() {cout?"调用函数CDerived::f3()! "?endl;} }; void main() {CBase objl,*p; 〃定义CBase的对象objl和指针对象p CDerived obj2; 〃定义CDerived 的对彖obj 1

C++虚函数相关练习及答案分析

1、A,B,C,D四个类,分别是B继承于A,C继承于B,其中B类中内嵌一D对象, 在main函数中构造一C对象,观测构造函数和析构函数的调用顺序。 代码: #include using namespace std; class A { public: A(){cout<<"调用A的构造函数"<

结果分析: 构造类C的对象时,因为C继承于B,B继承于A,且在B类中内嵌了D的对象,所以创建C的对象时,先调用A的构造函数,然后再调用内嵌对象d的构造函数,然后调用B的构造函数,然后调用C的构造函数,然后依次反向调用析构函数。当类之间存在继承关系时,创建对象时总是从最远(最里层)的类开始创建。 2、给上题A类增加一虚函数func,并要求B/C派生类也提供该函数实现,并实验分别以指针、引用 和普通对象方式访问该虚函数时的输出,并分析原因。 代码: #include using namespace std; class A { public: A(){cout<<"调用A的构造函数"<

西安交通大学18年9月课程考试《面向对象程序设计》作业考核试题

(单选题) 1: 下列说法错误的是()。 A: 在类中不能对所定义的数据成员进行初始化; B: 类的公有成员函数是类与外界的接口 C: 同一个类的两个对象占用相同的内存空间; D: 类实现数据隐藏。 正确答案: (单选题) 2: 下面是四条顺序出现的声明语句,非法的初始化语句是()。 A: int i = –1; B: const int ic=i; C: const int *pc=⁣ D: int *const cpi=⁣ 正确答案: (单选题) 3: 当一个类对象离开它的作用域时,系统自动调用该类的( )。 A: 无参构造函数 B: 带参构造函数 C: 拷贝构造函数 D: 析构函数 正确答案: (单选题) 4: 有关析构函数的说法不正确的是_____. A: 析构函数有且只有一个 B: 析构函数和构造函数一样可以有形参 C: 析构函数无任何函数类型 D: 析构函数的作用是在对象被撤销时收回先前分配的内存空间 正确答案: (单选题) 5: 下列哪个初始化是非法的(下面五个选项是顺序出现的声明语句)() A: int i =-1; B: const int ic=i; C: const int *pc=⁣ D: int *const cpi=⁣ E: const int *const cpic=⁣ 正确答案: (单选题) 6: 下列语句中错误的是()。 A: “int *p=new int (10);” B: “int *p=new int [10];” C: “int *p=new int ;” D: “int *p=new int (10)(0);” 正确答案: (单选题) 7: 如果类A被说明成类B的友元,则( ) A: 类A的成员即类B的成员 B: 类B的成员即类A的成员 C: 类A的成员函数不得访问类B的成员 D: 类B不一定是类A的友元 正确答案: (单选题) 8: 在一个类的定义中,包含有()成员的定义。 A: 数据 B: 函数 C: 数据和函数 D: 数据或函数 正确答案: (单选题) 9: 定义p并使p指向动态空间中的包含30个整数的数组所使用的定义语句为()。A: int *p=new int[30]; B: int *p=new int(30);

相关主题
文本预览
相关文档 最新文档