c++基础--c++默认生成的成员函数

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)

时间: 2024-08-26 03:02:58

c++基础--c++默认生成的成员函数的相关文章

类默认生成的成员函数

类默认生成的六个成员函数 一.构造函数 我们知道,类的数据成员是不能在声明类的时候初始化的,因为类并不是一个实体,而是一种抽象的数据类型,并不占据存储空间.为了解决这个问题,C++提供了构造函数来处理对象的初始化. 1.构造函数的作用 构造函数是一种特殊的成员函数,与其他成员函数不同,构造函数是在对象被实例化的时候自动被调用的,而且只执行这一次,它不能被用户调用.构造函数没有this指针. 构造函数的名字是固定的,与类名相同,不能由用户任意命名,它没有类型,没有返回值. 构造函数的功能是由用户自

C++中的默认成员函数

一般而言,对于一个用户自定义的类类型,以下四个函数在用户没有自定义的情形下,会由编译器自动生成: 1.default constructor 2.copy constructor Someclass::Someclass(const Someclass &); 3.copy assignment operator Someclass & Someclass::operator=(const Someclass &); 4.destructor 对于C++11,又增加了两个: 5.m

C++空类编译器自动生成的6个成员函数

一.问题 在C++中,编译器会为空类提供哪些默认成员函数?分别有什么样的功能呢? 二.详解 1.空类,声明时编译器不会生成任何成员函数 对于空类,编译器不会生成任何的成员函数,只会生成1个字节的占位符. 有时可能会以为编译器会为空类生成默认构造函数等,事实上是不会的,编译器只会在需要的时候生成6个成员函数:一个缺省的构造函数.一个拷贝构造函数.一个析构函数.一个赋值运算符.一对取址运算符和一个this指针. 代码: [html] view plaincopy #include <iostream

如何禁止C++默认成员函数

如何禁止C++默认成员函数 发表于 2016-03-02   |   分类于 C++  |   阅读次数 17 前言 前几天在一次笔试过程中被问到C++如何设计禁止调用默认构造函数,当时简单的想法是直接将默认构造函数声明为private即可,这样的话对象的确不能直接调用.之后查阅了<Effective C++>之后得到了比较详尽的解释. 了解C++的默认行为 当我们创建空类时,C++默认给我们生成了四种成员函数: 构造函数 析构函数 拷贝构造函数(copy) 重载=的拷贝函数(copy ass

读书笔记 effective c++ Item 45 使用成员函数模板来接受“所有兼容类型”

智能指针的行为像是指针,但是没有提供加的功能.例如,Item 13中解释了如何使用标准auto_ptr和tr1::shared_ptr指针在正确的时间自动删除堆上的资源.STL容器中的迭代器基本上都是智能指针:当然,你不能通过使用“++”来将链表中的指向一个节点的内建指针移到下一个节点上去,但是list::iterator可以这么做. 1. 问题分析——如何实现智能指针的隐式转换 真正的指针能够做好的一件事情是支持隐式转换.派生类指针可以隐式转换为基类指针,指向非const的指针可以隐式转换成为

成员函数与内联函数

11.关于成员函数特征的下列描述中,______是错误的.   成员函数一定是内联函数   B. 成员函数可以重载     C. 成员函数可以设置缺省参数值   D. 成员函数可以是静态的   [A] [解析] 因为成员函数可以在类体内定义,也可以在类体外定义,所以成员函数不一定都是内联函数:成员函数可以重载,可以设置参数的默认值,成员函数可以是静态的. 内联函数是指在调用衣柜函数时,不把他当作调用而处理,而是把这个函数的代码直接在调用他的函数里展开的形式,也可以理解为内联函数就相当于一段代码.

C++ 成员函数前和函数后加const修饰符区别

博客转载自: https://www.iteblog.com/archives/214.html 分析以下一段程序,阐述成员函数后缀const 和 成员函数前const 的作用 #include<iostream> using namespace std; class TestClass { public: size_t length() const; const char* getPContent(); void setLengthValid(bool isLengthValid); pri

【C++缺省函数】 空类默认产生的6个类成员函数

1.缺省构造函数. 2.缺省拷贝构造函数. 3. 缺省析构函数. 4.缺省赋值运算符. 5.缺省取址运算符. 6. 缺省取址运算符 const. <span style="font-size:18px;"> class A { public: A(){}//缺省构造函数 A(const A&){}//拷贝构造函数 ~A(){}//析构函数 A&operator=(const A&){}//赋值运算符 A*operator&(){}//取址运算

拷贝构造,深度拷贝,关于delete和default相关的操作,explicit,类赋初值,构造函数和析构函数,成员函数和内联函数,关于内存存储,默认参数,静态函数和普通函数,const函数,友元

 1.拷贝构造 //拷贝构造的规则,有两种方式实现初始化. //1.一个是通过在后面:a(x),b(y)的方式实现初始化. //2.第二种初始化的方式是直接在构造方法里面实现初始化. 案例如下: #include<iostream> //如果声明已经定义,边不会生成 class classA { private: int a; int b; public: //拷贝构造的规则,有两种方式实现初始化 //1.一个是通过在后面:a(x),b(y)的方式实现初始化 //2.第二种初始化的方式是直