Constructor
定义
构造器是类别基础的面向对象程序设计里的一种特殊子程序,用于在对象被创建时建立一个新对象,并为它的实例变量赋初值[1]。它与普通实例方法相似,但区别在于没有返回类型、不能被继承,作用域修饰符的规则也往往另有规定[3]。
构造器的名称通常与它所属的类同名:Java、C++、C#、ActionScript 等语言都采用这种命名规范,PHP 5 则建议使用 __construct[1]。它的核心任务有两项,即初始化对象的数据成员、建立并维持类所要求的不变式,当不变式无法成立时构造过程会失败[1]。对于不可变对象,全部初始化都必须在构造器内完成[1]。
从语言设计的角度,构造器解决的是“对象在使用前必须已初始化”这一可靠性问题。在 Java 一类语言中,编译器把无参构造器插入到类里,从而保证 new 表达式总能得到可用对象;在 C++ 中,编译器在对象定义处插入对构造器的调用,因此初始化不再依赖调用方是否记得额外调用 initialize 函数[4]。
原理
对象创建一般分为分配内存与初始化两步,构造器负责第二步。在 C++ 中,用 new 在堆上建立对象时先申请内存再调用构造器,建立栈上局部变量时先分配栈空间再调用构造器[5]。Java、C# 等语言把引用类型的实例放在堆中,由构造器在其中完成字段的赋值[1]。
构造器可以带参数,接收参数的称为参数化构造器,调用方式分为显式与隐式两种[1]。若类中没有显式声明构造器,多数语言会自动生成缺省构造器,其行为因语言而异:有的把实例变量初始化为 0 或空引用,有的什么也不做[1]。在 C++ 里,一旦定义了其他构造器,隐式生成的无参默认构造器就不复存在,需要时可用 =default 显式恢复[2]。
由于一个类可以拥有多个参数列表不同的构造器,编译器会依据实参的个数、类型与顺序来选择调用哪一个,这一机制称为构造器重载[6]。继承层次中的构造顺序是自顶向下的:派生类对象建立时先调用基类构造器,再按声明顺序初始化各数据成员,最后执行派生类构造器本身的函数体[2]。在 C++ 中,成员初始化列表在构造器函数体执行之前完成,并与函数体内的赋值语句在语义上不同[2]。
C++ 还提供了若干特殊构造器:复制构造器以同类对象的常量引用为参数,用已有对象生成副本;移动构造器随 C++11 引入,用于避免多余的内存分配、复制与销毁;此外还有从其他类型转换而来的转换构造器[1]。
发展历程
C++ 的构造器机制与其类概念同步形成。C++ 设计者本贾尼·斯特劳斯特卢普(Bjarne Stroustrup)在设计带类的 C 时,从 Simula 借用了 new 运算符,使其同时完成从自由存储区分配内存与调用构造器两项工作,以确保对象被初始化[7]。他在回顾中把“某物已被初始化”列为语言需要保证的要点之一[7]。
早期 C++ 的初始化写法对内置类型并不合法,后来通过赋予内置类型构造与析构的概念,int a(1); 这样的写法才被接受;设计者也考虑过允许从内置类型派生,最终没有采纳[8]。2011 年发布的 C++11 标准新增了移动构造器,配合右值引用减少了对象传递过程中的复制开销[9]。
在其他语言中,构造器的形态随语言演进而分化。PHP 5 规定使用 __construct 作为构造方法名,并保留同名方法作向下兼容,自 PHP 5.3.3 起该兼容途径只对非命名空间的类有效;Python 把构造过程拆为 new 与 init 两个方法,前者分配实例,后者初始化实例[1]。Perl 约定以 new 命名构造器,Object Pascal 用 constructor 关键字标识且允许自定名称,Objective-C 则把构造拆为 alloc 与 init 两步[1]。
应用
构造器在类库与框架设计中被用来强制对象进入合法状态。以 JavaScript 为例,构造函数可以定义一类对象的形状,其原型上的方法被所有实例共享,从而避免每个实例重复定义函数。MDN 的教程也把构造函数描述为创建类实例时执行的特殊函数,调用时通常在前面加 new 关键字[10]。
在 Java 等语言中,构造器还承担调用父类构造器的任务:子类构造器若未显式使用 super,编译器会隐式插入对父类无参构造器的调用;若父类没有无参构造器,则必须在子类构造器的首条语句中通过 super 指定参数[11]。同类的多个构造器之间也可以用 this 相互调用,并规定该语句位于首行[6]。
访问修饰符使构造器成为一种受控的创建入口。把构造器声明为 private 可以阻止外部通过 new 建立实例,Java 单例模式即利用这一点控制实例的产生[11]。C# 另有静态构造器,用于完成复杂的静态变量初始化,在类首次被访问时隐式执行,并具有线程安全性[12]。
局限
构造器只能负责单个对象的初始化,无法独立解决跨对象、跨资源的创建协调问题。当一个类只需暴露一种创建方式时,构造器就难以表达更复杂的分配策略,如复用对象池或依据多个类共同决定实例化何种子类,这类需求通常被抽象为工厂方法[1]。
构造器的不可继承性也带来维护上的负担。子类无法直接继承父类构造器,只能在其构造器内部调用,一旦父类增加了带参构造器而缺少无参构造器,原有子类代码会直接编译失败[11]。当类中存在多个仅在细节上略有差异的构造器时,阅读代码的人往往难以辨别每个构造器的用途[13]。
在 C++ 中,若构造器遗漏了对某个数据成员的初始化,得到的结果是未定义行为,编译器并不保证给出可预期的值,这在新增成员变量时尤其容易出错。默认的复制构造器执行浅复制,对于含有动态分配成员或资源句柄的类,析构时可能引发重复释放,因此往往需要同时自定义析构函数与复制赋值运算符[12]。此外,构造器是每个对象最先被调用的方法,在运行期被挂钩或注入的代码可以改变该类的初始化取值,从而影响其全部实例乃至子类[14]。
参见
参考资料
- 构造器 [引用日期2026-10-29]
- CPSC 427: Object-Oriented Programming [引用日期2026-10-29]
- wikipedia.org 上的网页 [引用日期2026-10-29]
- MFC Programmer's SourceBook [引用日期2026-10-29]
- C++ History [引用日期2026-10-29]
- 【编程基础知识】详解Java构造函数 [引用日期2026-10-29]
- BJARNE STROUSTRUP [引用日期2026-10-29]
- Revision f1e46d07-d0d1-4747-b185-19f49d0ce536 [引用日期2026-10-29]
- China-interview(PDF) [引用日期2026-10-29]
- 面向对象编程 [引用日期2026-10-29]
- java 构造器(构造方法)使用详细说明-华为开发者话题 | 华为开发者联盟 [引用日期2026-10-29]
- Constructor (object-oriented programming) [引用日期2026-10-29]
- citations [引用日期2026-10-29]
- Constructors - Chapters and Articles [引用日期2026-10-29]