[转载]关于隐式提供缺省构造函数的一个误区

很多C++的教材中都讲:“如果一个定义一个类,并且不提供任何构造函数的话,那么编译器将会隐式的提供一个缺省构造函数”。

以下节录ISO C++ 99的文档的原文:
The default constructor (12.1), copy constructor and copy assignment operator (12.8), and destructor (12.4)
are special member functions. The implementation will implicitly declare these member functions for a
class type when the program does not explicitly declare them, except as noted in 12.1. The implementation
will implicitly define them if they are used, as specified in 12.1, 12.4 and 12.8.

A default constructor for a class X is a constructor of class X that can be called without an argument. If
there is no user-declared constructor for class X, a default constructor is implicitly declared. An implicitly-
declared default constructor is an inline public member of its class.

An implicitly-declared default constructor for a class is implicitly defined when it is used to create an object
of its class type (1.8). The implicitly-defined default constructor performs the set of initializations of the
class that would be performed by a user-written default constructor for that class withan empty meminitializer-list (12.6.2) and an empty function body. If that user-written default constructor would be illformed,the program is ill-formed. Before the implicitly-declared default constructor for a class is implicitly
defined, all the implicitly-declared default constructors for its base classes and its nonstatic data members
shall have been implicitly defined. [Note: an implicitly-declared default constructor has an exceptionspecification(15.4).

以下摘录自“孙鑫vc教程的勘误表”:

我说:“C++又规定,如果一个类没有提供任何的构造函数,则C++提供一个默认的构造函数(由C++编译器提供)”,这句话也是错误的,正确的是:

如果一个类中没有定义任何的构造函数,那么编译器只有在以下三种情况,才会提供默认的构造函数:

1、如果类有虚拟成员函数或者虚拟继承父类(即有虚拟基类)时;

2、如果类的基类有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数);

3、在类中的所有非静态的对象数据成员,它们对应的类中有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数)。

C++ Primer(第3版)   14.2中也有这样的话:
        新用户常常会错误地认为:如果不存在缺省构造函数,则编译器会自动生成一个缺省构
造函数,并将其应用在对象上,以初始化类的数据成员,对于我们定义的Account   类来说这
就不是真的,系统既没有生成缺省构造函数也没有调用它。对于含有类数据成员或继承来的
比较复杂的类,这在部分上是对的,可能会生成一个缺省构造函数,但是它不会为内置或复
合型的数据成员如指针或数组提供初始值。

也可以从底层分析一下:

class A
{
public:
        int a;
        char *ptr;
};

int main()
{
        A a;
        return 0;
}

这种代码不会产生默认的ctor,此时的类就貌似C下面的struct,没有一个成员需要初始化。
证据1:

97:   int main()
98:   {
0040D680   push        ebp
0040D681   mov         ebp,esp
0040D683   sub         esp,48h
0040D686   push        ebx
0040D687   push        esi
0040D688   push        edi
0040D689   lea         edi,[ebp-48h]
0040D68C   mov         ecx,12h
0040D691   mov         eax,0CCCCCCCCh
0040D696   rep stos    dword ptr [edi]
99:       A a;                  //什么都没做
100:      return 0;
0040D698   xor         eax,eax
101:  }
0040D69A   pop         edi
0040D69B   pop         esi
0040D69C   pop         ebx
0040D69D   mov         esp,ebp
0040D69F   pop         ebp
0040D6A0   ret 

但是,

(1)如果加入需要构造函数的成员变量,则默认的ctor就会被调用,比如加入std::string

    #include <string>
    class A
    {
    public:
    int a;
    char *ptr;
    std::string test;
    };

    int main()
    {
    A a;
    return 0;

    }
int main()
99:   {
0040D680   push        ebp
0040D681   mov         ebp,esp
0040D683   sub         esp,5Ch
0040D686   push        ebx
0040D687   push        esi
0040D688   push        edi
0040D689   lea         edi,[ebp-5Ch]
0040D68C   mov         ecx,17h
0040D691   mov         eax,0CCCCCCCCh
0040D696   rep stos    dword ptr [edi]
100:      A a;
0040D698   lea         ecx,[ebp-18h];this指针
0040D69B   call        @ILT+35(A::A) (00401028);默认的ctor被调用
101:      return 0;
0040D6A0   mov         dword ptr [ebp-1Ch],0
0040D6A7   lea         ecx,[ebp-18h]
0040D6AA   call        @ILT+40(A::~A) (0040102d)
0040D6AF   mov         eax,dword ptr [ebp-1Ch]
102:  }
0040D6B2   pop         edi
0040D6B3   pop         esi
0040D6B4   pop         ebx
0040D6B5   add         esp,5Ch
0040D6B8   cmp         ebp,esp
0040D6BA   call        __chkesp (00401170)
0040D6BF   mov         esp,ebp
0040D6C1   pop         ebp
0040D6C2   ret 

(2)有虚函数的存在,也将调用默认的ctor,比如:

class A
{
public:
    int a;
    char *ptr;
    virtual void dd(){a=1;}
};

int main()
{
    A a;
    return 0;
}

相应的Assembly

100:      A a;
00401048   lea         ecx,[ebp-0Ch]
0040104B   call        @ILT+15(A::A) (00401014);这里面主要是将虚函数表首地址放入前面4个字节 

可以总结得出:编译器总是在需要ctor的时候,而用户没提供ctor的时候,才产生默认的ctor。这么做可以产生最优的效率。

时间: 2024-11-25 08:45:24

[转载]关于隐式提供缺省构造函数的一个误区的相关文章

effective c++条款13-17 “以对象管理资源”之C++隐式转换和转换构造函数

其实我们已经在C/C++中见到过多次标准类型数据间的转换方式了,这种形式用于在程序中将一种指定的数据转换成另一指定的类型,也即是强制转换,比如:int a = int(1.23),其作用是将1.23转换为整形1.然而对于用户自定义的类类型,编译系统并不知道如何进行转换,所以需要定义专门的函数来告诉编译系统改如何转换,这就是转换构造函数和类型转换函数! 注意:转换构造函数.隐式转换和函数对象不要搞混淆!!!函数对象是重载运算符(),和隐式转换函数易混淆. 一.转换构造函数 转换构造函数(conve

Scala隐式转换

概述 简单说,隐式转换就是:当Scala编译器进行类型匹配时,如果找不到合适的候选,那么隐式转化提供了另外一种途径来告诉编译器如何将当前的类型转换成预期类型.本文原文出处: http://blog.csdn.net/bluishglc/article/details/50866314 严禁任何形式的转载,否则将委托CSDN官方维护权益! 隐式转换有四种常见的使用场景: 将某一类型转换成预期类型 类型增强与扩展 模拟新的语法 类型类 语法 隐式转换有新旧两种定义方法,旧的定义方法指是的"impli

C++基础:缺省构造函数

缺省构造函数,又称默认构造函数,是C++以及其他的一些面向对象的程序设计语言中,对象的不需要参数即可调用的构造函数.下面将针对缺省构造函数的定义.使用.以及注意问题等方面简要探讨. 1.缺省构造函数是怎样的形式?是如何定义的? 在C++的一个类中,如果构造函数没有参数,或者构造函数的所有参数都有默认值,就可以称其为缺省构造函数.一个类中,只能有一个缺省构造函数. 对于如下代码:构造函数MyClass()没有参数,就是MyClass的缺省(默认)构造函数. class MyClass { publ

More Effective C++----(4)避免无用的缺省构造函数 &amp; (5)谨慎定义类型转换函数

Item M4:避免无用的缺省构造函数 缺省构造函数(指没有参数的构造函数)在C++语言中是一种让你无中生有的方法.构造函数能初始化对象,而缺省构造函数则可以不利用任何在建立对象时的外部数据就能初始化对象.有时这样的方法是不错的.例如一些行为特性与数字相仿的对象被初始化为空值或不确定的值也是合理的,还有比如链表.哈希表.图等等数据结构也可以被初始化为空容器. 但不是所有的对象都属于上述类型,对于很多对象来说,不利用外部数据进行完全的初始化是不合理的.比如一个没有输入姓名的地址簿对象,就没有任何意

为何要防止隐式类型转换

让编译器进行隐式类型转换所造成的弊端要大于它所带来的好处,所以除非你确实需要,不要定义类型转换函数. 隐式类型转换的缺点:它们的存在将导致错误的发生.例如:class Rational {public:  ...  operator double() const;                   // 转换Rational类成double类型};在下面这种情况下,这个函数会被自动调用:Rational r(1, 2);                            // r 的值是1

c++隐式类型转换和explicit

什么是隐式转换? 众所周知,C++的基本类型中并非完全的对立,部分数据类型之间是可以进行隐式转换的. 所谓隐式转换,是指不需要用户干预,编译器私下进行的类型转换行为.很多时候用户可能都不知道进行了哪些转换. 为什么要进行隐式转换? C++面向对象的多态特性,就是通过父类的类型实现对子类的封装. 通过隐式转换,你可以直接将一个子类的对象使用父类的类型进行返回. 在比如,数值和布尔类型的转换,整数和浮点数的转换等. 某些方面来说,隐式转换给C++程序开发者带来了不小的便捷. C++是一门强类型语言,

理解隐式类型、对象初始化程序和匿名类型

在C# 3.0中,几乎每个新特性都是为LINQ服务的.所以,本文将介绍下面几个在C# 3.0中引入的新特性: 自动实现的属性 隐式类型的局部变量 对象和集合初始化程序 隐式类型的数组 匿名类型 其实这几个特性都是比较容易理解的,对于这几个特性,编译器帮我们做了更多的事情(想想匿名方法和迭代器块),从而简化我们的代码. 自动实现的属性 在C# 3.0以前,当我们定义属性的时候,一般使用下面的代码 public class Book { private int _id; private string

慎用缺省构造函数的一种场景

本文通过示例来谈谈慎用缺省构造函数的一种设计场景.--以JAVA为例展开讨论. 为了便于讨论,我们假定需要建模一个Student,包括姓名和出生地两个属性.我们看到不少下面的代码: public class Student { private String name = null; private String birthPlace = null; public Student() { } public void setName(String name) { this.name = name;

[转]Activity详解 Intent显式跳转和隐式跳转

Activity 生命周期 显式 Intent 调用 1     //创建一个显式的 Intent 对象(方法一:在构造函数中指定) 2      Intent intent = new Intent(Intent_Demo1.this, Intent_Demo1_Result1.class); 3 4      Bundle bundle = new Bundle(); 5      bundle.putString("id", strID); 6      intent.putEx