什么是构造函数?构造函数可以重载吗?为什么?
发布网友
发布时间:2022-05-14 00:54
我来回答
共5个回答
热心网友
时间:2023-11-15 13:00
与类名称具有一样名称的成员函数是构造函数。构造函数不能有返回值,甚至不能有return语句。说明一个有返回值的构造函数是错误的,取构造函数的地址也是错误的。
如果一个类有构造函数,在程序中每个该类类型的对象在使用之前由此构造函数进行初始化(有关初始化的更多信息参见本章后面的“用特殊成员函数进行初始化”)。
构造函数是在对象的创建点上被调用的。创建对象可以是:
* 全局对象(文件范围或外部链接的)。
* 在一个函数或者小的封闭块中的局部变量。
* 用new运算符创建的动态对象。new操作在程序的堆或自由存储区中分配一个对象。
* 因显式调用构造函数而创建的临时对象(详见本章后面的“临时对象”)。
* 因编译器隐含调用构造函数而创建的临时对象(详见本章后面的“临时对象”)。
* 其它类的数据成员。在创建类类型的对象时,若此类类型由其它类类型变量组成,将会引起该类中每个对象的创建。
* 一个类的基类子对象。创建派生类类型的对象时会引起基类构件的创建。
构造函数的作用
一个构造函数执行各种任务,但对于程序员来说,这些任务是不可见的,你甚至可以不必为构造函数写任何代码。这些任务都同建立一个完全的、正确的类类型对象实例有关。
在MS C++中(同样也在很多其它C++中)一个构造函数:
* 初始化对象的虚拟基指针(vbptr)。如果该类是由虚拟基类派生出的,则这一步要执行。
* 按说明的顺序调用基类和成员的构造函数。
* 初始化对象的虚拟函数指针(vfptr)。如果该类有或者继承了虚拟函数,则这一步要执行,虚拟函数指针指向类的虚拟函数表(v-table),并且使虚拟函数的调用同代码正确绑定(binding)。
* 在构造函数体中执行可选的代码。
当构造函数结束以后,所分配的存储器就是一个给定类类型的对象。因为构造函数执行这些步骤,故虚拟函数的“迟后绑定”形态可以在虚拟函数的调用点得以解决,构造函数也要构造基类以及构造组合对象(作为数据成员的对象),迟后绑定是C++实现对象的多态行为的机制。
说明构造函数的规则
构造函数具有同类名相同的名称。只要遵守重载函数的规则(有关详情参见第12章“重载”),可以说明多个构造函数。
语法
类名称(参量说明表opt) cv-修饰符表opt
C++定义了两种类型的构造函数,缺省的和拷贝的构造函数。如表11.2所述。
表11.2 缺省的和拷贝构造函数
构造函数的种类 参量 目的
缺省构造函数 可以无参量调用 构造一个类类型的缺省对象
拷贝构造函数 可以接受对同种类类型的引用作为唯一参量 拷贝类类型的对象
缺省构造函数不要参量即可调用,但你可以说明一个带有参量表的缺省构造函数,只要让所有的参量有缺省值即可。同样,拷贝构造函数必须接受同一类类型的引用作为唯一参量。但可以提供更多的参量,只要后续的参量具有缺省值即可。
如果你不提供任何构造函数,编译器会试图生成一个缺省的构造函数。同样,如果你没有提供拷贝构造函数,编译器也会试图产生一个。编译器产生的构造函数视为公有的成员函数。如果你说明一个拷贝构造函数,而其第一个参量是一个对象而不是一个引用,则会产生错误。
编译器生成的缺省构造函数建立对象(初始化vftables和vbtables,如前面所述),并调用基类及成员的缺省构造函数,但不会采取其它的行动。基类和成员的构造函数只要存在,是可访问的,并且是无二义性的就会被调用。
编译器生成的拷贝构造函数建立一个对象,并且采用一个成员方式的拷贝来复制要被拷贝的对象的内容。如果基类或成员的构造函数存在,则它们将被调用,否则,就采取位方式的拷贝。
如果所有的基类和该类类型的成员类具有接受一个常量参量的构造函数,则编译器生成的拷贝构造函数接受一个唯一的参量的类型是const type&;否则,编译器生成的拷贝构造函数接受的唯一参量的类型是type &。
你可以用一个构造函数去初始化一个const和volatile对象,但构造函数本身不能说明为const和volatile的。对于构造函数唯一合法的存储类型inline,对于构造函数使用任何其它的存储类修饰符,包括__declspec关键字都会引起错误。构造函数和析构函数除了__stdcall外不能说明为其它的调用约定。
派生类是不能继承基类中的构造函数的。当一个派生类的对象在创建的时候,它是从基类构件开始构造的,然而才进入派生类构件。编译器使用每个基类的构造函数作为完整对象初始化的一部分(除了虚拟派生有所不同,参见本章后面的“初始化基类”)。
显式地调用构造函数
在程序中,为创建给定类型的对象,可以显式地调用构造函数。例如:创建两个Point型对象的描述一条线段的端点,可以写如下代码:
DrawLine(Point(13,22),Point(87,91);
创建了两个Point对象,传递给DrawLine函数,并在该表达式(函数调用)结束后拆除。
另外一个显式地调用构造函数的情况是在一个初始化中:
Point pt=Point(7,11);
创建了一个Point类型的对象,并用接受两个整形参量的构造函数进行初始化。如前面的两个例子中,通过显式地调用构造函数创建的对象是无名的对象,并在它们所创建的表达式中是有生存期的。在本章后面的“临时对象”中将对这一点进行深入的讨论。 在构造函数内调用成员函数和虚拟函数
在构造函数里面调用任何成员函数通常是很安全的,因为在执行第一行用户代码之前,对象已经完全建立起来了(已经初始化了虚表等等)。但是当成员函数调用了其抽象基类的虚拟成员函数时,在构造函数和析构函数调用此成员函数存在着潜在的不安全性。
构造函数可以调用虚拟函数。在调用虚拟函数时,被调用的是在构造函数自身的类中定义的函数(或者从其基类中继承的函数)。下面的例子显示了一个虚拟函数在一个构造函数中被调用时发生的情况。
#include <iostream.h>
class Base
{
public:
Base();//缺省构造函数
virtual void f(); //虚拟成员函数
};
Base::Base()
{
cout<<"Constructing Base sub-object\n ";
f();//在构造函数中调用虚拟成员函数
}
void Base::f()
{
cout<<"called Base::f()\n";
}
class Derived:public Base
{
public:
Derived();//缺省构造函数
void f(); //该类虚拟函数的实现
};
Derived::Derived()
{
cout<<"constructing Derived object\n";
}
void Derived::f()
{
cout<<"Called Derived::f()\n";
}
void main()
{
Derived d;
}
在上面的程序运行的时候,Derived d的说明会引发下列一系列事件:
1. 类Derived的构造函数(Derived::Derived)被调用。
2. 在进入到Derived类的构造体之前,基类Base的构造函数被调用。
3. Base::Base调用函数f,它是一个虚拟函数。通常被调用的函数会是Derived::f,因为对象d是Derived类型的对象。但因为Base::Base是一个构造函数,此时的对象是一个Derived类型的对象,故Base::f将会被调用。
构造函数与数组
数组的构造只能使用缺省的构造函数。缺省构造函数要么不接受任何参量,要么对于它的所有参量都有缺省的值。数组通常是按升序来构造的,该数组的每一个成员的初始化都是使用同一构造函数。
构造的次序
对于派生类或其成员数据是类类型的类,构造发生的顺序有助于你理解,在任一给定的构造函数中你能够使用对象的哪一部分。
构造与继承
一个派生类的对象是从基类到派生类通过按次序为每个类调用构造函数来构造的。
每个类的构造函数能仅依赖于被完全构造好的它的基类。
有关初始化的完整描述,包括初始化的顺序,见本章后面的“初始化基类及成员”。
构造与组合类型
含有类类型数据成员的类称为组合类。当创建一个组合类类型的对象时,含有类的构造函数在该类的构造函数之前调用。 有关这种情况的初始化,见本章后面的“初始化基类及成员”。
热心网友
时间:2023-11-15 13:00
上面那个太多了,明显是网上摘的,我的解释是:构造函数就是与类名有相同名称的成员函数,即:类名是a,该类中含有名字为a的成员函数,构造函数是可以重载的。
热心网友
时间:2023-11-15 13:01
楼上的牛人啊
构造函数就是 public 类名(参数列表)
{
//方法主体
}
它在你new 这个类的对象的时候被自动调用,就这么简单,一般用于给对象初始化的时候赋值或者读取配置文件备用
热心网友
时间:2023-11-15 13:01
对于构造方法的定义。一楼已经说得比较清楚了。关于重载问题,构造方法当然可以了,正如二楼所描述的。但你还要理解重载是怎样构成的。
热心网友
时间:2023-11-15 13:02
给你的例子,你应该就能明白拉
public class Test{
Test(){
System.out.println("test");
}
Test(String s){
System.out.println("test "+s);
}
public static void main (String main[]){
Test t=new Test();
Test t2=new Test("r");
}
}