第七章:构造函数面试题目专题

一、下面初始值是错误的,请找出问题所在并尝试修改它。

    struct X
    {
        X(int i,int j):base(i),rem(base%j){ }
        int rem,base;
    }
    
   解析:     
	此类问题经常考察的知识点为:
	知识点1:类内成员是const、引用时或者使用某种类类型没有默认构造函数的成员时必须将其初始化。
	知识点2:初始化和赋值是不等价的。
	知识点3:成员初始化的顺序一般是没有要求的,但是若用一个成员来初始化另一个成员是,就必须考虑到其定义时的顺序(写时保持顺序一直即可)。
	答案为;此题出现了知识点3中的问题:用一个成员来初始化另一个成员,没有考虑顺序问题。

二、使用本节提供的Sales_data类,确定初始化下面的变量时分别使用了哪些构造函数,然后罗列出每个对象所有数据成员的值。

 Sales_data first_item(cin);
 int main()
 {
      Sales_data next;
      Sales_data last("9-999-99999-9");
 }
 解析: 答案:第一个调用的是第三种构造函数;第二个调用的是默认的构造函数(默认参数已经在237页的代码中设定);
       第三种调用的是第一种构造参数。
       本题目引申出来的知识点为:构造函数的特点(1)构造函数的名子必须和类名相同,不能任意命名; (2) 构造函数没有返回值; (3)构造函数可以被重载,但是每次对象创建时只会调用其中的一个。
       我们需要注意的是:可以将构造总结为以下几类,不同入参决定调用不同的构造函数,它们分别是**普通构造函数** 、**默认构造函数** 、**拷贝构造函数** 、**转换构造函数**;分别学习一下以下构造函数。
       
       1.普通构造函数
       数普通构造函数是最为常见的构造函数,它没有特殊的特点,其函数形参参数可有多个,其函数原型和测试代码如下:
       
										//普通构造函数原型
										CComplex(int nReal, int nImag);
										//测试代码
										//需要指定参数
										CComplex num1(1,1);
										num1.PrintComplex();
	   2. 默认构造函数
	  相对于普通构造函数而言,默认构造函数是指用户可以指定实参值,也可以不指定实参值,不指定实参值,系统就使用默认的值,
	  而且默认构造函数只可以有一个,否则有歧义;默认构造函数原型和测试代码如下:

										//默认构造函原型声明
										CComplex();   //默认构造函数形式1,这里选择形式1
										CComplex(int i=10,int j=10);//默认构造函数形式2
										//调用默认构造函数,用户可以不需要指定形参值
										CComplex sum;
										sum.PrintComplex();

       3. 拷贝构造函数
拷贝构造函数,也叫做复制构造函数,主要应用于使用一个已存在的对象去初始化一个新对象,使新对象的属性和该对象保持一致。
若用户未定义拷贝构造函数,编译器就自动提供一个默认的复制构造函数,其作用仅是简单地复制类中的每个数据成员;
为了安全起见,类设计者需要提供拷贝构造函数;
拷贝构造函数一般形式:
										类名(const 类名& 对象名)
										形如:CComplex(const CComplex& srcObj)
 (1)在以下三种情况下,拷贝构造函数将被调用;
 
 a. 建立新对象
 使用一个已存在对象num,初始化即将要被创建的新对象,可以有以下两种形式:
 CComplex num1(num); //形式1
 CComplex num2=num;  //形式2
 b. 函数形参为类对象
在函数调用时需要将实参对象完整地传递给形参,系统将默认调用拷贝构造函数,建立一个实参的拷贝,这样形参对象和实参对象将具有相同的属性值;需要注意的是,仅形参是按值传递时才会进行对象拷贝,若是传递引用,不会进行对象拷贝,
	//按值传递,复制对象
	static void TransByValue(CComplex obj)
	//传递引用,不复制对象
	static void TransByRefence(const CComplex& obj)
c. 函数返回值是类对象
当一个函数返回值是一个类对象时,在该函数返回调用处时,系统会将函数中的对象复制一份到别的地方,即使返回值没有被使用;
						static CComplex GetObj()
						{
						  CComplex ret(10,10);  
						  return ret;
						}
     4. 转换构造函数
转换构造函数的作用是将一个其他类型的数据转换为一个类的对象。转换构造函数也是一种构造函数,它遵循构造函数的一般原则,我们通常把仅有一个参数的构造函数用作类型转换,所把它称为转换构造函数。
转换构造函数中的类型数据可以是普通类型,也可以是类类型,其一般形式如下:
类名(指定类型的数据)

							//转换构造函数
							CComplex(int nReal)
    5. 对象拷贝和对象赋值的区别
对象的赋值是对一个已经存在的对象进行重新赋值,因此必须先定义被赋值的对象,才能进行**赋值**; 
而对象的**拷贝**是在从无到有建立一个新对象,并使这个新对象和已有对象完全相同; 

							CComlex num1(1,1);
							CCompex num2(2,2);
							//对象赋值 使对象num2的值和对象num1一致,调用进行operator=运算符重载
							num2 = num1;
							//对象拷贝,利用对象num2创建新对象num3;
							CCompex num3 = num2;

三、如果接受一string的构造函数和接受istream& 的构造函数都是用默认构造实参,这种行为合法吗?如果不,为什么?

		          不合法,调用的时候不知道调用哪个了,编译器分辨不了。

四、从下面的概念中选择一个(或者你自己指定一个),思考这样的类需要哪些成员,提供一组合理的构造函数并说明这样做的原因。

             (a)Book              (b)Date            (c)Employee         (d)Vehicle           (e)Object           (f)Tree
		     Employee:数据成员:company/name/age/address/salary/position/.......
		     构造函数中company可以设置为默认参数,其他的可以进列表初始化。

五、委托构造函数

  委托构造函数:一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程。
					class OH
					{
					OH(string s, int a, int b):book(s),price(a),sale(b){} //三参数构造函数的参数列表和函数体首先被执行
					OH():OH("",0,0);//默认构造函数又委托给了三参数构造函数
					OH (istream& is):OH(){}//OH (istream& is)是一个委托构造函数,它委托给了默认构造函数OH()
					string book;
					int price;
					int sale;
					};
知识点2:当构造函数委托给另一个构造函数时,受委托的构造函数的初始值列表及其函数体先被执行,接着才会执行到委托函数的函数体。
					class OH{
					OH(string s, int a, int b):book(s),price(a),sale(b){cout<<"OH(string s, int a, int b)"<<endl;} //三参数构造函数的参数列表和函数体首先被执行
					OH():OH(" ",0,0);//默认构造函数又委托给了三参数构造函数
					OH (istream& is):OH(){cout<<"OH (istream& is)"<<endl;}//OH (istream& is)是一个委托构造函数,它委托给了默认构造函数OH()
					string book;
					int price;
					int sale;
					};

六、默认构造函数的重要性。

         知识点1:默认构造函数在很多情况下也是非常重要的
		1:当类的作用域内不需要使用任何初始值定义非静态变量时 
		2:当类中含有需要使用默认构造函数当作被委托构造函数时 
		3:类的成员没有在构造函数初始值列表中显示初始化时 
		知识点2:当类中定义了其它类型的构造函数时,最好提供一个默认的构造函数,养成良好的习惯。 
		知识点3:类声明对象是不需要加括号!加括号是声明函数的。
		例子: OH oh();//不合法的实例化
		       OH oh;//合法

七、下面这条声明合法吗?如果不,为什么?

       因为没有显示初始化,此时向量中的十个元素都需要默认初始化,但是类NoDefault没有默认的构造函数,所以错误。

八、下面哪些论断是不正确的?为什么?

(a)一个类至少提供一个构造函数。
(b)默认构造函数是参数列表为空的构造函数。
(c)如果对于类来说不存在有意义的默认值,则类不应该提供默认构造函数。
(d)如果类没有默认构造函数,则编译器将为其生成一个并把每个数据成员初始化成相应类型的默认值。

解析:   (a):错误,类本身不提供构造函数时,编译器会自动合成一个默认构造函数 。
		(b):错误,为成员提供默认值的构造函数也成为默认构造函数 。
		(c):错误,默认构造函数在很多情况下也是非常重要的。
		(d):错误,当类没有定义构造函数时,才会默认编译器生成默认构造函数。

九、说明接受一个string参数的Sale_data构造函数是否应该是explicit的,并解释这样做的优缺点。

	解析:
	知识点1:如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制。此构造函数被称为转换构造函数。
	知识点2:通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的机制。 
	知识点3:可以将构造函数声明为explicit加以限定该类型转换。
	优缺点:缺点:这样的隐式类型转换只能作用于单参数的构造函数。
	优点:相较于两步的显示转换,这样的隐式转换则会省去很多麻烦。

十、对于combine函数的三种不同声明,当我们调用i.combine(s)时分别发生了什么情况?其中i是一个Sale_data,而s是一个string对象。

			(a)Sales_data &combine(Sales_data);
			(b)Sales_data &combine(Sales_data&);
			(c)Sales_data &combine(const Sales_data&) const;
			解析:(a)合法 
                  (b)不合法,Salesdata&类型与Salesdata类型之间不可转换 
                  (c)不合法,const不对,因为combine本身是需要改变传入参数的

十一、vector 将其参数的构造函数定义成explicit的,而string则不是,你觉的原因应该是?

			int getSize(const std::vector<int>&);
			//这样的使用是否显得比较迷惑
			getSize(34);

十二、使用Sales_data类,解释下面的初始化过程。如果存在问题请尝试修改它。

           Sale_data item = {"978-0590353403",25,15.99};
            知识点:用户可以直接访问成员、且有独特的初始化语法被称为**聚合类**,有以下几个特点:
			1:所有成员皆public ;
			2:没有定义任何的构造函数 ;
			3:没有类内初始值 ;
			4:没有基类,也没有虚函数;
			若想以此方式初始化该类,则需要将原类修改成聚合类,并且成员的顺序需要明确。

十二、字面值类型知识点

		知识点1:字面值常量类:数据成员都是字面值类型的聚合类(还有其他几个特例)
		知识点2:字面值类型:算术类型、指针、引用,不是字面值类型的有:IO库、String类型 
		知识点3:字面值常量类的构造函数可以是constexpr,一般来说为空

十三、下面的Data类是字面值常量类吗?请解释原因?

					struct Data
					{
					   int ival;
					   string s;
					}
	解析:是字面值常量类。原因为:具体解释如下:字面值常量类有两种情况 
					1:数据成员都是字面值类型的聚合类 
					2:满足四个条件的类
					(1)数据成员都必须是字面值类型;
					(2)类至少含有一个constexpr构造函数;
					(3)如果一个数据成员含有类内初始值,则内置类型成员的初始值必须是一条常量表达式,
					或者某种成员属于某种类型,则初始值必须使用成员自己的constexpr;
					(4)类必须使用析构函数默认定义,该成员负责销毁的对象;
					本题的data类中的string成员也是字面值类型的。

猜你喜欢

转载自blog.csdn.net/N1314N/article/details/89418699