C++派生类的构造函数
- 格式:docx
- 大小:21.08 KB
- 文档页数:6
1.1在C++中,三种派生方式的说明符号为public、private、protected不加说明,则默认的派生方式为private。
1.2当公有派生时,基类的公有成员成为派生类的公有成员;保护成员成为派生类的保护成员;私有成员成为派生类的不能直接访问成员。
当保护派生时,基类的公有成员成为派生类的保护成员;保护成员成为派生类的保护成员;私有成员成为派生类的不能直接访问成员。
1.3 派生类的构造函数一般有3项工作要完成:首先基类初始化,其次成员对象初始化,最后执行派生类构造函数体。
1.4多继承时,多个基类中的同名的成员在派生类中由于标识符不唯一而出现二义性。
在派生类中采用虚基类或作用域分辨符来消除该问题。
2.简答题2.1 派生类如何实现对基类私有成员的访问?2.2什么是类型兼容规则?2.3派生类的构造函数是怎样的执行顺序,析构函数的执行顺序是如何实现的?2.4继承与组合之间的区别与联系是什么?2.5什么是虚基类?它有什么作用?含有虚基类的派生类的构造函数有什么要求,什么是最远派生类,建立一个含有虚基类的派生类的对象时,为什么由最远派生类的构造函数负责虚基类的初始化?3.选择题3.1下面对派生类的描述中,错误的是(D )。
A.一个派生类可以作为另外一个派生类的基类B.派生类至少有一个基类C.派生类的成员除了它自己的成员外,还包含了它的基类的成员D.派生类中继承的基类成员的访问权限到派生类中保持不变3.2下列对友元关系叙述正确的是(A)。
A.不能继承B.是类与类的关系C.是一个类的成员函数与另一个类的关系D.提高程序的运行效率3.3当保护继承时,基类的(B)在派生类中成为保护成员,不能通过派生类的对象来直接访问。
A.任何成员B.公有成员和保护成员C.公有成员和私有成员D.私有成员3.4设置虚基类的目的是(B)。
A.简化程序B.消除二义性C.提高运行效率D.减少目标代码3.5在公有派生情况下,有关派生类对象和基类对象的关系,不正确的叙述是( C )。
C++语法总结(一)1. I/O流的常用控制符dec 置基数为10hex 置基数为16oct 置基数为8setfill(c) 设填充字符为Csetprecision(n) 设显示小数精度为n位setw(n) 设域宽为N个字符setiosflags(ios::fixed) 固定的符点显示setiosflags(ios::scientific)指数表示setiosflags(ios::left) 左对齐setiosflags(ios::right) 右对齐setiosflags(ios::skipws) 忽略前导空白setiosflags(ios::uppercase) 16进制数大写输出setiosflags(ios::lowercase) 6进制数小写输出setiosflags(ios::showpoint) 显示小数点setiosflags(ios::showpos) 正数前面加上正号2.头文件:*iostream.h* *iomanip.h* *stdlib.h*cout/cin 流的控制符 exit(0)3.指针的几种类型:int (*p)();p为指向函数的指针变量,该函数带回一个整形值int *p();p为带回一个指针的函数,该指针指向整形数据int (**)[n]p为一个指向一个指针变量的指针变量,被指向的指针变量指向一个含n个整形数据的一维数组4.构造函数和析构函数特点及其区别:a.构造函数可以有任意个形参,还可以重载(多个参数个数不同的函数);但析构函数不能有形参,因为是系统自动调用的.b.构造函数不可以任意调用,只准系统调用;而析构函数不仅系统调用,也可以任意调用.5.构造函数和析构函数什么时候自动运行?(例61)构造函数:一般在定义类对象时自动运行.析构函数:如果一个函数中定义了一个对象,则在这个函数运行结束时就执行一次;当一个对象是使用NEW运算符被动态创建的,在使用DELETE运算符释放它时,DELETE将会自动调用析构函数.拷贝初始化构造函数:当用tpoint N(M);语句时调用一次;当对象作为实参向形参传递时,即对形参初始化时执行一次;当遇到M=return(N);语句,即对M进行初始化时调用一次;6. this 指针用法:例63中,当程序执行语句list elem(i);时,系统对this指针进行了如下的缺省赋值:this=&list;赋值成员函数举例(此例在例63基础上):void Assign(linear_list&);//说明语句;void linear_list::Assign(linear_list&p){if(&p==this)return;nmax=p.nmax;nelem=p.nelem;list=new int[nmax];for(int i=0;i<nmax;i++)list[i]=p.list[i];}7.const,volatile对象和const,volatile成员函数格式:const person per/volatile person per;int func(int) const;/char func(int) volatile;说明:1.普通对象既可以访问const对象,又可以访问volatile对象;2.const对象只能访问用const修饰的成员函数;volatile对象只能访问用其所长volatile修饰的成员函数;3.也可以同时说明为const volatile对象或const volatile成员函数;const volatile对象只能访问const volatile成员函数;const/volatile对象也能访问const volatile成员函数;8.不同继承方式的基类和派生类特性------------------------------------------------继承方式 | 基类特性 | 派生类特性------------------------------------------------| public | public公有继承 | protected | protected| private | 不可访问------------------------------------------------| public | private私有继承 | protected | private| private | 不可访问------------------------------------------------| public | protected保护继承 | protected | protected| private | 不可访问------------------------------------------------A:帮助理解:1)对于公有继承方式:a.基类成员对其对象的可见性:公有成员可见,其他不可见。
C语言里面构造函数和析构函数的运用办法C语言里面构造函数和析构函数的运用办法摘要:构造函数与析构函数是一个类中看似较为简单的两类函数,但在实际运用过程中总会出现一些意想不到的运行错误。
本文将较系统的介绍构造函数与析构函数的原理及在C#中的运用,以及在使用过程中需要注意的若干事项。
关键字:构造函数;析构函数;垃圾回收器;非托管资源;托管资源一.构造函数与析构函数的原理作为比C更先进的语言,C#提供了更好的机制来增强程序的安全性。
C#编译器具有严格的类型安全检查功能,它几乎能找出程序中所有的语法问题,这的确帮了程序员的大忙。
但是程序通过了编译检查并不表示错误已经不存在了,在“错误”的大家庭里,“语法错误”的地位只能算是冰山一角。
级别高的错误通常隐藏得很深,不容易发现。
根据经验,不少难以察觉的程序错误是由于变量没有被正确初始化或清除造成的,而初始化和清除工作很容易被人遗忘。
微软利用面向对象的概念在设计C#语言时充分考虑了这个问题并很好地予以解决:把对象的初始化工作放在构造函数中,把清除工作放在析构函数中。
当对象被创建时,构造函数被自动执行。
当对象消亡时,析构函数被自动执行。
这样就不用担心忘记对象的初始化和清除工作。
二.构造函数在C#中的运用构造函数的名字不能随便起,必须让编译器认得出才可以被自动执行。
它的命名方法既简单又合理:让构造函数与类同名。
除了名字外,构造函数的另一个特别之处是没有返回值类型,这与返回值类型为void的函数不同。
如果它有返回值类型,那么编译器将不知所措。
在你可以访问一个类的方法、属性或任何其它东西之前,第一条执行的语句是包含有相应类的构造函数。
甚至你自己不写一个构造函数,也会有一个缺省构造函数提供给你。
class TestClass{public TestClass(): base() {} // 由CLR提供}下面列举了几种类型的构造函数1)缺省构造函数class TestClass{public TestClass(): base() {}}上面已介绍,它由系统(CLR)提供。
c++派生类的构造函数C++是一门面向对象的编程语言,它允许程序员使用类和对象来封装数据和行为。
在C++中,派生类是基于已存在的类(称为基类)创建的一种新类。
派生类从基类继承了数据和方法,并且还可以添加新的属性和方法。
在C++中,派生类的构造函数是指创建派生类对象时所调用的函数。
派生类的构造函数负责初始化派生类对象中从基类继承的成员和派生类自己添加的成员。
本文将详细介绍C++派生类的构造函数。
在C++中,派生类的构造函数必须调用基类的构造函数,以初始化从基类继承的成员变量。
在创建派生类对象时,首先创建基类对象,然后再对派生类对象进行初始化。
1. 构造函数必须有与类名相同的名称。
2. 构造函数可以有参数,也可以没有参数。
3. 派生类必须调用基类的构造函数,以初始化从基类继承的成员变量。
下面是一个基类Person和一个派生类Student的定义:```cppclass Person{protected:string name;int age;public:Person(){}void setName(string n){name = n;}void setAge(int a){age = a;}};在定义派生类Student的时候,通过public继承了基类Person。
此时,派生类的构造函数必须调用基类的构造函数,以初始化从基类继承的成员变量name和age。
派生类新增加了一个成员变量grade,需要在自己的构造函数中进行初始化。
派生类构造函数可以有多种调用方式,具体如下:1. 用基类构造函数初始化列表初始化派生类对象初始化列表是C++语言提供的一种简洁的初始化成员变量的语法。
它使用冒号(:)后接一个以逗号分隔的初始化列表,在其中对派生类和基类成员变量进行初始化。
下面是Student类中使用初始化列表对基类成员变量进行初始化的方法:在上面的代码中,派生类Student的构造函数使用冒号后接一个初始化列表来初始化基类成员变量name和age。
5.2 基类和派生类在C++中,当一个类被其他类继承时,被继承的类称为基类(base class)。
继承其他类特性的类称为派生类(derived class)。
从本质上看,基类是具有一个类集合中的公共特性,派生类在继承基类特性的同时可以加入自己独有的特性。
基类与派生类之间反映出下述三种不同的现象:(1)派生类是基类的具体化。
即模拟概念层次,表示“is-a”的关系。
(2)派生类是基类的延迟定义。
可以定义一个抽象基类,定义一些操作,使它们服从一定的协议,但许多可能并未实现,然后定义非抽象的派类,实现抽象基类中定义的行为。
这时派生类不是基类的具体化,而是抽象类的实现。
在JA V A中,有专门的纯虚类,称为接口,其作用就是为不同的类提供一个统一的接口,同时间接实现多继承(JA V A不支持多继承)。
(3)派生类是基类的结合。
当一个派生类有多于一个的基类时,它们组合在一起形成具有所有基类行为的类型。
这时要注意,不要用继承表达聚合关系。
5.2.1 基类与派生类的说明先看一个例子。
[例5.1] 派生类的说明EX5_1.CPP。
继承基类的派生类定义的一般形式:class derived_class_name:access_specifier base_class_name{……};其中access_specifier可以是3个关键字之一:public、private(默认值)或protected。
派生类也称为子类、导出类。
它具有下述特点:(1)可在基类所提供有基础上包含新成员;(2)可在自己类中隐藏基类的任何成员;(3)为新类重新定义基类中的函数;[例5.2] 子类的特点EX5_2.CPP。
5.2.2 派生类的继承权与访问域派生类的继承权如果不能有效在加以限制,就不能按照实际情况表达求解问题的复杂性。
因此访问权限是一个很重要的问题。
(1)对于基类的私有成员,派生类及派生类的使用者无权访问。
(2)对于基类的公有成员,则按派生类的定义,分为三种情况:①私有派生,继承基类的公有成员作为自己的私有成员,这些成员只能被派生类的成员函数访问。
【奥鹏】[西安交通大学]西交《面向对象程序设计》在线作业试卷总分:100 得分:100第1题,在下列的各类函数中,( )不是类的成员函数。
A、构造函数B、析构函数C、友元函数D、拷贝构造函数正确答案:C第2题,在创建派生类对象时,构造函数的执行顺序是( )。
A、对象成员构造函数、基类构造函数、派生类本身的构造函数;B、派生类本身的构造函数、基类构造函数、对象成员构造函数;C、基类构造函数、派生类本身的构造函数、对象成员构造函数;D、基类构造函数、对象成员构造函数、派生类本身的构造函数。
正确答案:D第3题,友元的作用之一是( )。
A、提高程序的运行效率。
B、加强类的封装性。
C、实现数据的隐蔽性。
D、增加成员函数的种类。
正确答案:A第4题,由C++目标文件连接而成的可执行文件的缺省扩展名为( )。
A、cppB、exeC、objD、lik正确答案:B第5题,用new运算符创建一个含有10个元素的一维整型数组的正确语句是( )。
A、int p=new int(10);B、int p=new int[10];C、int *p=new int[10];D、int *p=new int(10);正确答案:C第6题,以下关于函数模板的论述中,正确的是( )。
A、一旦定义了函数模板,就不能再定义同名的重载函数;B、从同一个函数模板实例化后得到的多个模板函数属于函数重载;C、函数模板中只有一种参数;D、在定义函数模板时,template语句与函数模板定义语句之间可以有别的语句。
正确答案:B第7题,已知:p是一个指向类A数据成员m的指针,A1是类A的一个对象。
如果要给A1的m赋值为5,( )是正确的。
A、A1.p=5;B、A1-p=5;C、A1.*p=5;D、*A1.p=5;正确答案:C第8题,一个类的构造函数为“B(int ax, int bx): a(ax), b(bx) {}”,执行“B x(1,2),y(3,4);x=y;”语句序列后x.a的值为( )。
派生类的构造函数派生类是指从基类继承而来的子类,它们在继承基类的同时,可以增加自己的成员变量和成员函数。
在C++中,派生类的构造函数是一种特殊的函数,用于初始化派生类的对象。
本文将介绍派生类的构造函数的基本概念、特点和使用方法。
一、派生类的构造函数的基本概念派生类的构造函数是一种特殊的函数,它用于初始化派生类的对象。
它可以调用基类的构造函数来初始化基类的成员变量,也可以初始化派生类自己的成员变量。
派生类的构造函数必须在定义派生类时进行声明和定义。
在定义派生类的构造函数时,需要指定它所继承的基类的构造函数的调用方式。
如果不指定,编译器会默认调用基类的默认构造函数。
如果基类没有默认构造函数,那么编译器会报错。
派生类的构造函数和基类的构造函数一样,可以有多个,但是它们之间必须有不同的参数列表。
如果没有定义派生类的构造函数,那么编译器会自动生成一个默认的构造函数。
二、派生类的构造函数的特点1. 派生类的构造函数必须先调用基类的构造函数,再初始化自己的成员变量。
2. 如果派生类的构造函数没有显式地调用基类的构造函数,编译器会默认调用基类的默认构造函数。
3. 如果基类没有默认构造函数,那么派生类的构造函数必须显式地调用基类的构造函数,并且在参数列表中传递必要的参数值。
4. 派生类的构造函数可以调用基类的构造函数的任何一个版本,包括默认构造函数、拷贝构造函数和移动构造函数。
5. 派生类的构造函数可以覆盖基类的构造函数,但是必须保证派生类的构造函数与基类的构造函数的参数列表不同。
6. 派生类的构造函数可以调用自己的其他构造函数,但是必须保证调用顺序正确,不会出现死循环。
三、派生类的构造函数的使用方法1. 显式地调用基类的构造函数派生类的构造函数可以显式地调用基类的构造函数,以初始化基类的成员变量。
调用基类的构造函数的方法有两种:一种是在派生类的构造函数的初始化列表中调用,另一种是在派生类的构造函数的函数体中调用。
c++ 派生类构造函数C++是一种面向对象的编程语言,提供了派生类(子类)的概念,允许我们在已有类的基础上进行扩展并添加新的行为和属性。
派生类的构造函数是创建和初始化派生类对象时调用的函数。
本文将介绍C++中派生类构造函数的概念、使用方法和注意事项。
在C++中,每个类都有一个构造函数,用于创建和初始化该对象。
派生类继承了基类的成员变量和成员函数,但是派生类需要自己的构造函数来初始化它自己的成员变量。
派生类构造函数既可以调用基类构造函数来初始化基类成员变量,也可以初始化自己的成员变量。
派生类构造函数有以下特点:1.派生类构造函数的函数名必须与类名相同。
2.派生类构造函数必须在其成员初始化列表中调用基类构造函数。
3.派生类构造函数只能直接或间接调用基类构造函数,不能调用基类的析构函数。
二、派生类构造函数使用方法1.调用基类构造函数派生类默认情况下会继承基类的构造函数,因此,派生类的构造函数需要在函数体前调用基类的构造函数,以初始化基类成员变量。
调用基类构造函数的写法为构造函数名::构造函数名(参数列表) : 基类构造函数名(参数列表)。
例如下面的代码:```class Base {public:Base(int n) {this->n = n;}protected:int n;};在上面的代码中,Derived继承了Base的属性和方法,但Base的构造函数需要通过Derived的构造函数进行调用。
在Derived构造函数的函数体中,我们可以定义自己的成员变量,并为它们赋初值。
2.初始化自己的成员变量除了要调用基类的构造函数外,派生类的构造函数还要初始化自己的成员变量。
派生类构造函数的成员初始化列表用于初始化自身的成员变量。
例如:在上面的代码中,Derived类有一个名为m的私有成员变量,会在Derived的构造函数中被初始化。
1.在执行派生类构造函数时,如果基类和派生类都有默认构造函数,则默认情况下会先调用基类的默认构造函数,再调用派生类的默认构造函数。
c++名词解释题类的派生在C++编程中,类的派生是一个重要的概念。
派生类是从一个已有的基类派生出来的新类,它可以继承基类的成员变量和成员函数,并且可以在派生类中添加自己的成员变量和成员函数。
派的派生过程是通过使用关键字“class”和“base class”实现的。
派生类与基类的联系在于,派生类可以继承基类的公共和保护成员,这意味着在派生类中,我们可以直接使用基类的成员变量和成员函数。
这种继承关系使得代码的复用变得更加方便。
然而,派生类与基类也有区别,派生类拥有自己的成员变量和成员函数,可以看作是基类的扩展。
当创建派生类时,需要调用基类的构造函数和析构函数。
这是因为基类的构造函数和析构函数需要在派生类中执行,以确保基类的成员变量和成员函数能够正确初始化和清理。
同时,派生类也可以拥有自己的构造函数和析构函数,它们可以与基类的构造函数和析构函数相互独立。
在派生类中,成员的访问权限分为三种:公共、保护和私有。
公共成员在派生类中可以被外部访问,保护成员可以在派生类中被访问,但外部无法直接访问。
私有成员在派生类中只能被内部访问。
这种访问权限的设置可以实现对基类成员的封装和保护,防止外部直接操作基类的成员。
当派生类与基类有同名成员时,可以通过使用作用域分辨率符“::”来区分。
在派生类中,我们可以使用基类名加上作用域分辨率符来访问基类的同名成员。
下面我们通过一个实例来分析如何使用类的派生实现功能。
假设我们有一个基类“Shape”,其中包含两个公共成员变量:圆半径(radius)和面积(area)。
我们还可以在基类中定义一个成员函数,计算并返回面积。
```cppclass Shape {public:double radius;double area() {return 3.14 * radius * radius;}};```接下来,我们创建一个派生类“Circle”,从基类“Shape”派生。
在派生类中,我们添加一个公共成员变量“circleRadius”,用于存储圆的半径。
一、选择题A(1)下列的()是引用调用。
A.形参是引用,实参是变量;B.形参和实参都是变量;C.形参是指针,实参是地址值;D.形参是数组名,实参是数组名。
C(2)作用域运算符的功能是()A.给定作用域的大小;B.表示作用域的级别的;C.某个成员是属于哪个类的;D.指出作用域的范围的。
D(3)下列的各函数中,()不是类的成员函数。
A.构造函数;B.析构函数;C.拷贝初始化构造函数;D.友元函数。
D(4)下面()不是构造函数的特征。
A.构造函数可以重载;B.构造函数可以设置缺省参数;C.构造函数的函数名和类名相同;D.构造函数必须指定返回值类型。
C(5)下述静态数据成员的特性中,()是错误的。
A.静态数据成员要在类体外进行初始化;B.说明静态数据成员时前边要加修饰符static;C.静态数据成员不是所有对象所共有的;D.引用静态数据成员时,要在静态数据成员名前加<类名>和作用域运算符。
C(6)已知类A有三个公有成员:void f1(int), void f2(int) 和int a,则()是指向类A成员函数的指针。
A.A *p; B. int A::*pc=&A::a;C.void(A ::*pa)(int);D. A **p;C(7)下列关于对象数组的描述中,()是错的。
A.对象数组的数组名是一个地址常量;B.对象数组的下标是从0开始的;C.对象数组只能赋初值,不能被赋值;D.对象数组的数组元素是同一个类的对象.B(8)下列定义中,()是定义指向类A的对象数组的指针。
A.A *p[5];B.A (*p)[ 5];C.(A *) p[5]; D.A *p[ ];A(9)说明语句const char *ptr;中,ptr是()。
A.指向字符常量的指针;B.指向字符的常量指针;C.指向字符串常量的指针;D.指向字符串的常量指针。
B(10)关于new运算符的下列描述中,( )是错的.A.使用它创建对象时要调用构造函数;B.使用它创建对象数组时必须指定初始值;C.它可以用来动态创建对象和对象数组;D.使用它创建的对象或对象数组可以使用运算符delete删除。
1.下列程序中横线处正确的语句应该是()#include<iostream>using namespace std;class Base{public;void fun( ){cout< < “Base : : fun” < < endl;}};class Derived : public Base{public:void fun( ){_________//显示调用基类的函数fun( )cout < < “Derived : : fun” < < endl;}};A fun( ).B Base.fun( )C Base : : fun( )D Base - >fun( )2下面程序的执行结果是()#include<iostream.h>class A{public:void disp(){ cout<<"class A"<<endl;}};class B:public A{public:void disp(){cout<<"class B"<<endl;}};void main(){B b;b.disp();}输出为:class B3下面程序的执行结果是()#include<iostream.h>class A{public:A(int I,int j){a=I;b=j;}void move(int x,int y){a+=x;b+=y;}void show(){cout<<"("<<a<<","<<b<<")"<<endl;} private:int a,b;};class B:private A{ public:B(int I,int j,int k,int l):A(I,j){x=k;y=l;}void show(){cout<<x<<","<<y<<endl;}void fun(){move(2,4);}void f1(){A::show();}private:int x,y;};void main(){ A e(2,3);e.show();B d(4,5,6,7);d.fun();d.show();d.f1();d.show();}输出为:(2,3)6,7(6,9)4下面程序的输出结果为#include<iostream.h>class base{public:base(int x,int y){a=x;b=y;}void show(){cout<<"base:"<<a<<";"<<b<<endl;} private:int a,b;};class derived:public base{public:derived(int x,int y,int z):base(x,y),c(z){}void show(){cout<<"derived:"<<c<<endl;} private:int c;};int main(){base b(10,10),*pb;derived d(20,30,40);pb=&b;pb->show();pb=&d;pb->show();return 0;}输出结果为base:10;10base:20;3055、下面程序的输出结果为#include<iostream.h>class AA{protected:int k;public:AA(int n=4):k(n){}~AA(){cout<<"AA"<<endl;}virtual void f() const=0;};inline void AA::f() const{}class BB:public AA{public:~BB(){cout<<"BB"<<endl;}void f() const{cout<<k-2;AA::f();}};int main(){AA &p=*new BB;p.f();delete &p;return 0;}程式的执行结果2AA6 分析以下程式的执行结果:#include<iostream.h>class base{ int n;public:base(){};base (int a){cout << "constructing base class" << endl;n=a;cout << "n=" << n << endl;}~base() { cout << "destructing base class" << endl; } };class subs : public base{int m;public:subs(int a, int b) : base(a){cout << "constructing sub class" << endl;m=b;cout << "m=" << m << endl;}subs() { cout << "destructing sub class" << endl; }};void main (){subs s(1,2);}解:这里base 是基类,subs为派生类,subs类的构造函数中含有调用基本类的构造函数。
派生类的构造函数
派生类的构造函数是派生类的特征,它主要用于创建派生类的实例,继承自基类的构造函数及其它动作,并支持对派生类进行必要的
定制。
它有三种形式:默认构造函数、参数构造函数和拷贝构造函数。
默认构造函数:派生类如果没有显式定义构造函数,则系统会自
动生成一个无参的默认构造函数,该函数不接受任何参数,只执行基
类相关的构造操作,并对派生类新增的成员使用默认初始化策略。
参数构造函数:参数构造函数需要用户自己定义,它可以接受一
个或多个参数,构造函数体中包含了基类相关的构造操作,以及其它
一些初始化操作。
拷贝构造函数:拷贝构造函数也是需要用户自己显式定义的,它
可以接受一个参数为已有的对象,构造函数体内包含基类相关的构造
操作,及将参数对象的值拷贝到新的对象中去的必要的操作。
派生类的构造函数在程序设计中非常重要,使用正确的构造函数可以
使类的使用更加方便,并避免可能出现的错误。
派生类构造函数可以
根据实际需要来定义,但是最好以默认构造函数或参数构造函数为基础,以此来解决派生类的大部分初始化工作,有时候也可以考虑采用
拷贝构造函数来避免重复初始化和赋值操作。
csingledoctemplate 构造函数-回复构造函数是一种特殊的函数,它在创建类的实例时被调用。
构造函数在C++中非常重要,它用于初始化对象的数据成员并执行其他必要的操作。
本文将详细介绍构造函数的定义、使用和特性,以及为什么它们在面向对象编程中非常有用。
第一部分:构造函数的定义和用途构造函数是一种与类同名的特殊函数,没有返回类型(甚至不能声明返回类型),并且没有明确的返回值。
构造函数的主要用途是初始化对象的数据成员,以确保对象在创建后处于有效的状态。
构造函数可以具有任意数量的参数,这些参数用于传递初始值给对象的数据成员。
通过调用构造函数并传递适当的参数,可以创建一个具有已设置初始值的对象。
构造函数的语法如下:cppclass ClassName {public:ClassName(type1 arg1, type2 arg2, ...) {初始化对象的数据成员并执行其他操作}其他成员函数和数据成员的定义};在上述语法中,`ClassName`是类的名称,`arg1`,`arg2`等是构造函数的参数名称和类型。
构造函数可以具有默认参数,这样在创建对象时可以不提供对应的参数值。
这使得构造函数更加灵活,可以在不同的情况下提供不同的默认行为。
构造函数也可以重载,即用不同的参数列表定义多个构造函数。
这样,根据提供的参数类型和数量,可以调用适合的构造函数来创建对象。
第二部分:构造函数的调用和顺序构造函数在创建类的实例时自动调用,无需显式地调用。
当通过关键字`new`在堆上创建对象时,构造函数将在内存中分配适当的空间,并初始化对象的数据成员。
在栈上声明类的对象时,构造函数会自动调用,初始化对象的数据成员。
构造函数的调用顺序是基于继承关系的。
在派生类的构造函数中,首先调用基类的构造函数,然后再调用派生类自己的构造函数。
继承链上每个类的构造函数都会被依次调用。
在使用构造函数初始化列表时,可以显式地指定数据成员的初始值。
C++与QT试题选择题(共20题)每题5分1.C++语言对C语言做了很多改进,C++语言相对于C语言的最根本的变化是( )。
A.增加了一些新的运算符B.允许函数重载,并允许设置缺省参数C.规定函数说明符必须用原型D.引进了类和对象的概念2.下列程序的运行结果为( )。
#includevoid main(){int a=2:int b=a+1;cout<<}A.0.66667 B.0C.0.7 D.0.66666666…3.下列关于运算符重载的叙述中,正确的是( )。
A.通过运算符重载,可以定义新的运算符B.有的运算符只能作为成员函数重载C.若重载运算符+,则相应的运算符函数名是+D.重载一个二元运算符时,必须声明两个形参4.下列关于模板的叙述中,错误的是( )。
A.调用模板函数时,在一定条件下可以省略模板实参B.可以用int、double这样的类型修饰符来声明模板参数C.模板声明中的关键字class都可以用关键字typename替代D.模板的形参表中可以有多个参数5.下列描述中,不属于面向对象思想主要特征的是( )。
A.封装性B.跨平台性C.继承性D.多态性6.要利用C++流进行文件操作,必须在程序中包含的头文件是( )。
A.iostreamB.fstreamC.strstreamD.Iomanip7.下面的哪个选项不能作为函数的返回类型? ( )。
A.voidB.intD.Long8.函数fun的返回值是( )。
fun(char*a,char*B.{int num=0,n=0;while(*(a+num)!=‘\0’)num++;while(b[n]){*(a+num)=b[n];num++;n++;}return num;}A.字符串a的长度B.字符串b的长度C.字符串a和b的长度之差D.字符串a和b的长度之和9.有如下函数定义:void func (int a,int&b) {a++; b++;}若执行代码段:int x=0 ,y=1func(x,y);则变量x和y值分别是( )。
C++派⽣类与基类构造函数的调⽤顺序问题以下为转载感谢原作者关于派⽣类构造函数与基类构造函数的调⽤顺序问题,我们先看⼀下书上的说法:《⾯向对象程序设计基础(第⼆版》李师贤等,第254页:C++语⾔的基本规则是:创建⼀个派⽣类的对象时,如果基类带有构造函数,则先调⽤基类的构造函数,然后才调⽤派⽣类的构造函数。
《Thinking in C++》,刘宗⽥等译,第261页:可以看出,构造在类层次的最根处开始,⽽在每⼀层,⾸先调⽤基类构造函数,然后调⽤成员对象构造函数。
《C++ Primer Plus(第四版)中⽂版》,孙建春等译,第399页:记住:创建派⽣类对象时,程序⾸先调⽤基类构造函数,然后再调⽤派⽣类构造函数。
真的是这样吗?⼀个类的对象在实例化时,这个类的构造函数会被调⽤。
如果承认这⼀点,就会发现上述论断的⽭盾之处。
⼀个派⽣类的对象,在实例化时,不调⽤作为产⽣它的类的构造函数,⽽先去调⽤别的类的构造函数,这符合逻辑吗?再考虑⼀下基数构造函数有参数的的时候,派⽣类构造函数的定义形式,“派⽣类构造函数可以使⽤初始化列表机制将值传递给基类构造函数”(《C++ Primer Plus(第四版)中⽂版》第399页)。
如果是基类的构造函数先被调⽤,那么它所使⽤的参数从何⽽来?前两本书在说明这⼀规则时,毫⽆例外地在派⽣类构造函数和基类构造函数中使⽤cout输出⼀些信息来表明相应的构造函数被调⽤了,并以此说明构造函数的调⽤顺序。
在这⾥,我要指出的是:这⼀顺序,仅仅是这些cout输出的顺序,并不能说明是函数调⽤的顺序。
真正调⽤的过程,单纯依赖于C++是看不到的。
我们可以⽤这样的实验来证明这⼀点。
选择前两本书关于这⼀规则的任何⼀个实例,在Visual Studio中,分别对派⽣类和基类的构造函数下断点,注意:断点要下在函数定义函数名处,这样才是真正函数执⾏的起点,⽽不能下在cout语句上,那是函数体,不能说明问题。
然后调试这个程序,你会发现派⽣类构造函数的断点先中断,基类的构造函数断点后中断。
C++考试题(选择题)1、选择题1、___A__只能访问静态成员变量。
A 静态函数B 虚函数C 构造函数D 析构函数2、下列的各类函数中,__C___不是类的成员函数。
A 构造函数B 析构函数C友元函数 D 拷贝构造函数3、友元的作用_A__。
A 提高程序的运行效率B 加强类的封装性C 实现数据的隐藏性D 增加成员函数的种类4、类模板的使用实际上是将类模板实例化成一个具体的_D____。
A 类B 对象C 函数D 模板类5、下列函数中,___C__不能重载。
A 成员函数B 非成员函数C 析构函数D 构造函数6、___C__是一个在基类中说明的虚函数,它在该基类中没有定义,但要求任何派生类都必须定义自己的版本。
A 虚析构函数B虚构造函数C纯虚函数 D 静态成员函数7、__A___是istream的派生类,处理文件输入;___C__是iostream的派生类,可以同时处理文件的I/O。
A、ifstreamB、ostreamC、fstreamD、ofstream8、对于派生类的构造函数,在定义对象时构造函数的执行顺序为:先执行__A___,再执行__B___,后执行__C___。
A 成员对象的构造函数B 基类的构造函数C 派生类本身的构造函数9、局部变量可以隐藏全局变量,那么在有同名全局变量和局部变量的情形时,可以用__A___提供对全局变量的访问。
A 域运算符B 类运算符C 重载D 引用10、一个__C___允许用户为类定义一种模式,使得类中的某些数据成员及某些成员函数的返回值能取任意类型。
A 函数模板B 模板函数C 类模板D 模板类11、系统在调用重载函数时,往往根据一些条件确定哪个重载函数被调用,在下列选项中,不能作为依据的是___D__。
A 参数个数B 参数的类型C 函数名称D函数的类型12、如果一个类至少有一个纯虚函数,那么就称该类为__A___。
A 抽象类B 虚基类C 派生类D 以上都不对13、进行文件操作时需要包含__B___文件。
前面我们说基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承。
构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成为派生类的构造函数,当然更不能成为普通的成员函数。
在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有private 属性的成员变量,它们在派生类中无法访问,更不能使用派生类的构造函数来初始化。
这种矛盾在C++继承中是普遍存在的,解决这个问题的思路是:在派生类的构造函数中调用基类的构造函数。
下面的例子展示了如何在派生类的构造函数中调用基类的构造函数:
1.#include<iostream>
ing namespace std;
3.
4.//基类People
5.class People{
6.protected:
7.char*m_name;
8.int m_age;
9.public:
10.People(char*,int);
11.};
12.People::People(char*name,int age):m_name(name),m_age(age){}
13.
14.//派生类Student
15.class Student:public People{
16.private:
17.float m_score;
18.public:
19.Student(char*name,int age,float score);
20.void display();
21.};
22.//People(name, age)就是调用基类的构造函数
23.Student::Student(char*name,int age,float score):People(name, age),m_score(score){}
24.void Student::display(){
25.cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<"。
"<<endl;
26.}
27.
28.int main(){
29.Student stu("小明",16,90.5);
30.stu.display();
31.
32.return0;
33.}
运行结果为:
小明的年龄是16,成绩是90.5。
请注意第23 行代码:
1.Student::Student(char*name,int age,float score):People(name, age),m_score(score){}
也可以将基类构造函数的调用放在参数初始化表后面:
1.Student::Student(char*name,int age,float score):m_score(score),People(name, age){}
但是不管它们的顺序如何,派生类构造函数总是先调用基类构造函数再执行其他代码(包括参数初始化表以及函数体中的代码),总体上看和下面的形式类似:
1.Student::Student(char*name,int age,float score){
2.People(name, age);
3.m_score = score;
4.}
当然这段代码只是为了方便大家理解,实际上这样写是错误的,因为基类构造函数不会被继承,不能当做普通的成员函数来调用。
换句话说,只能将基类构造函数的调用放在函数头部,不能放在函数体中。
另外,函数头部是对基类构造函数的调用,而不是声明,所以括号里的参数是实参,它们不但可以是派生类构造函数参数列表中的参数,还可以是局部变量、常量等,例如:
1.Student::Student(char*name,int age,float score):People("小明",16),m_score(score){}
构造函数的调用顺序
从上面的分析中可以看出,基类构造函数总是被优先调用,这说明创建派生类对象时,会先调用基类构造函数,再调用派生类构造函数,如果继承关系有好几层的话,例如:
那么创建C 类对象时构造函数的执行顺序为:
构造函数的调用顺序是按照继承的层次自顶向下、从基类再到派生类的。
还有一点要注意,派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的。
以上面的A、B、C 类为例,C 是最终的派生类,B 就是C 的直接基类,A 就是C 的间接基类。
C++ 这样规定是有道理的,因为我们在C 中调用了B 的构造函数,B 又调用了A 的构造函数,相当于C 间接地(或者说隐式地)调用了A 的构造函数,如果再在C 中显式地调用A 的构造函数,那么A 的构造函数就被调用了两次,相应地,初始化工作也做了两次,这不仅是多余的,还会浪费CPU时间以及内存,毫无益处,所以C++ 禁止在C 中显式地调用A 的构造函数。
基类构造函数调用规则
事实上,通过派生类创建对象时必须要调用基类的构造函数,这是语法规定。
换句话说,定义派生类构造函数时最好指明基类构造函数;如果不指明,就调用基类的默认构造函数(不带参数的构造函数);如果没有默认构造函数,那么编译失败。
请看下面的例子:
1.#include<iostream>
ing namespace std;
3.
4.//基类People
5.class People{
6.public:
7.People();//基类默认构造函数
8.People(char*name,int age);
9.protected:
10.char*m_name;
11.int m_age;
12.};
13.People::People():m_name("xxx"),m_age(0){}
14.People::People(char*name,int age):m_name(name),m_age(age){}
15.
16.//派生类Student
17.class Student:public People{
18.public:
19.Student();
20.Student(char*,int,float);
21.public:
22.void display();
23.private:
24.float m_score;
25.};
26.Student::Student():m_score(0.0){}//派生类默认构造函数
27.Student::Student(char*name,int age,float score):People(name, age),m_score(score){}
28.void Student::display(){
29.cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<"。
"<<endl;
30.}
31.
32.int main(){
33.Student stu1;
34.stu1.display();
35.
36.Student stu2("小明",16,90.5);
37.stu2.display();
38.
39.return0;
40.}
运行结果:
xxx的年龄是0,成绩是0。
小明的年龄是16,成绩是90.5。
类的哪一个构造函数,从运行结果可以很明显地看出来,系统默认调用了不带参数的构造函
的输出结果将变为:
xxx的年龄是0,成绩是90.5。
如果将基类People 中不带参数的构造函数删除,那么会发生编译错误,因为创建对象stu1 时需要调用People 类的默认构造函数,而People 类中已经显式定义了构造函数,编译器不会再生成默认的构造函数。