C++中的空类,默认产生哪些类成员函数?

C++中的空类,默认产生哪些类成员函数?

转载自 yb223732 http://www.cnblogs.com/ltang/archive/2010/10/08/1861145.html
 
答:

class Empty

{

  public:

      Empty(); // 缺省构造函数

      Empty( const Empty& ); // 拷贝构造函数

      ~Empty(); // 析构函数

       Empty& operator=( const Empty& ); // 赋值运算符

       Empty* operator&(); // 取址运算符

       const Empty* operator&() const; // 取址运算符 const

};

默认构造函数 
析构函数 
拷贝构造函数 
赋值运算符(operator=) 
取址运算符(operator&)(一对,一个非const的,一个const的) 

     当然,所有这些只有当被需要才会产生。比如你定义了一个类,但从来定义过该类的对象,也没使用过该类型的函数参数,那么

基本啥也不会产生。在比如你从来没有进行过该类型对象之间的赋值,那么operator=不会被产生。

class Empty
{
public:
          Empty();                  //   缺省构造函数

          Empty(const   Empty&);    //   拷贝构造函数

          ~Empty();                 //   析构函数

          Empty& perator=(const Empty&); //   赋值运算符

           Empty* operator&();              //   取值运算符
          const Empty* operator&() const;   //   取值运算符

};


例如有以下class:

class StringBad
{
 private :
         char   * str;
          int len;

 public :
           StringBad( const   char   * s);
           StringBad();
           ~ StringBad();

} ;


在构造函数和析构函数定义当中有如下定义:

StringBad::StringBad( const   char   * s)
{
         len = std::strlen(s);
         str =   new   char [len +   1 ];

}

StringBad::StringBad()
{
        len =   4 ;
        str =   new   char [ 4 ];

}

StringBad:: ~ StringBad()
{

        delete [] str;
}

    那么在程序当中如果有以下代码:

    StringBad sports( " Spinach Leaves Bow1 for bollars " );

    StringBad sailor = sports;

    以上的第二条初始化语句将会调用什么构造函数?记住,这种形式的初始化等效于下面的语句:

    StringBad sailor = StringBad(sports);

    因为sports的类型为StringBad,因此相应的构造函数原型应该如下:

    StringBad( const StringBad & );

      当我们使用一个对象来初始化另一个对象时,编译器将自动生成上述构造函数(称为复制构造函数,因为它创建对象的一

个副本)

      现在我们来看看我们没有定义复制构造函数的情况下调用隐式复制构造函数将会出现什么情况。

      从构造函数定义的代码片断可以看到,当中使用new操作符初始化了一个指针str,而隐式的复制构造函数是按值进行复制

的,那么对于指针str,将会进行如下复制:

      sailor.str = sports.str;

       这里复制的不是字符串,而是一个指向字符串的指针!也就是说,我们将得到两个指向同一个字符串的指针!由此会产生的

问题将不言而喻。当其中一个对象调用了析构函数之后,其str指向的内存将被释放,这个时候我们如果调用另一个对象,其

str指向的地址数据会是什么?很明显将会出现不可预料的结果。

       所以由此可见,如果类中包含了使用new初始化的指针成员,应当定义一个复制构造函数,以复制指向的数据,而不是指针,

被称为深度复制。因为默认的浅复制(或成为成员复制)仅浅浅的赋值指针信息。

      我们再看以下代码片断,我们稍做修改:

      StringBad headline1( " Celery Stalks at Midnight " );

      StringBad knot;

      knot = headline1;

       这里的最后一行将与以上例子有所区别,现在是将已有对象赋给另一个已有对象,这将会采取其他操作,即使用重载的赋值

操作符。(我们需要知道的是:初始化总是会调用复制构造函数,而使用=操作符时也可能调用赋值操作符)因为C++允许对

象赋值,这是通过自动为类重载赋值操作符实现的。其原型如下:

      Class_name & Class_name:: operator   = ( const Class_name & );

      它接受并返回一个指向类对象的引用。

      与隐式的复制构造函数一样,隐式的对象赋值操作符也会产生同样的问题,即包含了使用new初始化的指针成员时,只会采

用浅复制。所以我们需要使用同样的解决办法,即定义一个重载的赋值操作符来实现深度复制。

      所以综上所述,如果类中包含了使用new初始化的指针成员,我们应该显式定义一个复制构造函数和一个重载的赋值

操作符来实现其深度复制,避免由此带来的成员复制问题

    1. 以下函数哪个是拷贝构造函数,为什么?

    X::X(const X&);

    X::X(X);

    X::X(X&, int a=1);

    X::X(X&, int a=1, b=2);

    2. 一个类中可以存在多于一个的拷贝构造函数吗?

    3. 写出以下程序段的输出结果, 并说明为什么? 如果你都能回答无误的话,那么你已经对拷贝构造函数有了相当的了解。

#include <iostream>
#include <string>

struct X {
template<typename T>
      X( T& ) { std::cout << "This is ctor." << std::endl; }

template<typename T>
      X& perator=( T& ) { std::cout << "This is ctor." << std::endl; }
};

void main() {
      X a(5);
      X b(10.5);
      X c = a;
      c = b;
}

 

    解答如下:

    1. 对于一个类X,如果一个构造函数的第一个参数是下列之一:

    a) X&

    b) const X&

    c) volatile X&

    d) const volatile X&

    且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数。

    X::X(const X&); //是拷贝构造函数

    X::X(X&, int=1); //是拷贝构造函数

   2.类中可以存在超过一个拷贝构造函数,

class X {
public:
      X(const X&);
      X(X&);            // OK
};
注意,如果一个类中只存在一个参数为X&的拷贝构造函数,那么就不能使用const X或volatile X的对象实行拷贝初始化。

class X {
public:
     X();
     X(X&);
};

const X cx;
X x = cx;    // error

    如果一个类中没有定义拷贝构造函数,那么编译器会自动产生一个默认的拷贝构造函数。

    这个默认的参数可能为X::X(const X&)或X::X(X&),由编译器根据上下文决定选择哪一个。

    默认拷贝构造函数的行为如下:

    默认的拷贝构造函数执行的顺序与其他用户定义的构造函数相同,执行先父类后子类的构造。

    拷贝构造函数对类中每一个数据成员执行成员拷贝(memberwise Copy)的动作。

    a)如果数据成员为某一个类的实例,那么调用此类的拷贝构造函数。

    b)如果数据成员是一个数组,对数组的每一个执行按位拷贝。

    c)如果数据成员是一个数量,如int,double,那么调用系统内建的赋值运算符对其进行赋值。

    3. 拷贝构造函数不能由成员函数模版生成。

struct X {
template<typename T>
      X( const T& );    // NOT copy ctor, T can't be X

 template<typename T>
      perator=( const T& ); // NOT copy ass't, T can't be X
};

        原因很简单,成员函数模版并不改变语言的规则,而语言的规则说,如果程序需要一个拷贝构造函数而你没
有声明它,那么编译器会为你自动生成一个。所以成员函数模版并不会阻止编译器生成拷贝构造函数, 赋值运算
符重载也遵循同样的规则。(参见Effective C++ 3edition, Item45)
### C++ 默认成员函数列表 在 C++ 中,默认成员函数是由编译器根据需要自动生成的函数。这些函数通常在没有显式定义时由编译器提供,以支持对象的构造、析构、拷贝和移动等操作。以下是 C++ 默认成员函数列表[^1]: #### 1. 默认构造函数 (Default Constructor) 默认构造函数是在没有为定义任何构造函数时,由编译器生成的一个无参构造函数。它用于创建对象而不进行任何初始化。 ```cpp class MyClass { public: // 编译器生成的默认构造函数 // MyClass(); }; ``` #### 2. 析构函数 (Destructor) 析构函数在对象生命周期结束时被调用,用于释放资源。如果没有显式定义析构函数,编译器会生成一个默认的析构函数。 ```cpp class MyClass { public: // 编译器生成的默认析构函数 // ~MyClass(); }; ``` #### 3. 拷贝构造函数 (Copy Constructor) 拷贝构造函数用于通过另一个同型的对象初始化当前对象。如果没有显式定义拷贝构造函数,编译器会生成一个逐成员拷贝的默认实现。 ```cpp class MyClass { public: // 编译器生成的默认拷贝构造函数 // MyClass(const MyClass&); }; ``` #### 4. 拷贝赋值运算符 (Copy Assignment Operator) 拷贝赋值运算符用于将一个对象赋值给另一个对象。如果没有显式定义拷贝赋值运算符,编译器会生成一个逐成员赋值的默认实现。 ```cpp class MyClass { public: // 编译器生成的默认拷贝赋值运算符 // MyClass& operator=(const MyClass&); }; ``` #### 5. 移动构造函数 (Move Constructor) [C++11 及以后版本] 移动构造函数用于通过右值引用(rvalue reference)初始化对象,从而避免不必要的拷贝操作。如果没有显式定义移动构造函数,编译器会生成一个默认的移动构造函数。 ```cpp class MyClass { public: // 编译器生成的默认移动构造函数 // MyClass(MyClass&&); }; ``` #### 6. 移动赋值运算符 (Move Assignment Operator) [C++11 及以后版本] 移动赋值运算符用于将一个右值引用赋值给当前对象,从而避免不必要的拷贝操作。如果没有显式定义移动赋值运算符,编译器会生成一个默认的移动赋值运算符。 ```cpp class MyClass { public: // 编译器生成的默认移动赋值运算符 // MyClass& operator=(MyClass&&); }; ``` #### 注意事项 - 如果中定义了某些特定的默认成员函数(例如用户定义的拷贝构造函数),编译器可能不会生成其他相关的默认成员函数(例如移动构造函数)。这种行为取决于 C++ 标准版本以及的定义方式。 - 对于内置型成员变量,可以在初始化列表中直接提供初始值,也可以使用花括号进行列表初始化(C++11 及以后版本)。但是,不能使用空括号 `()` 来初始化内置型的成员变量,因为内置型没有默认构造函数[^2]。 ```cpp class MyClass { private: int member; public: MyClass(int x) : member(x) {} // 使用初始化列表初始化 member }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值