一、 构造函数是干什么的

  1.  
    class Counter
  2.  
    {
  3.  
    public:
  4.  
             // 类Counter的构造函数
  5.  
             // 特点:以类名作为函数名,无返回类型
  6.  
             Counter()
  7.  
             {
  8.  
                    m_value = 0;
  9.  
             }
  10.  
    private:
  11.  
              // 数据成员
  12.  
             int m_value;
  13.  
    }

该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数->由构造函数完成成员的初始化工作
eg:    Counter c1;
       编译系统为对象c1的每个数据成员(m_value)分配内存空间,并调用构造函数Counter( )自动地初始化对象c1的m_value值设置为0
故:构造函数的作用:初始化对象的数据成员。
二、 构造函数的种类

  1.  
    class Complex 
  2.  
    {         
  3.  
    private :
  4.  
            double    m_real;
  5.  
            double    m_imag;
  6.  
    public:
  7.  
    Complex() //一般构造函数
  8.  
            {
  9.  
                 m_real = 0.0;
  10.  
                 m_imag = 0.0;
  11.  
            }   
  12.  
     
  13.  
    Complex(double real, double imag) //一般构造函数
  14.  
            {
  15.  
                 m_real = real;
  16.  
                 m_imag = imag;         
  17.  
             }  
  18.  
     
  19.  
    Complex(const Complex & c) //拷贝构造函数
  20.  
            {
  21.  
                    // 将对象c中的数据成员值复制过来
  22.  
                    m_real = c.m_real;
  23.  
                    m_imag    = c.m_imag;
  24.  
            }        
  25.  
    Complex &operator=( const Complex &rhs ) //赋值构造函数
  26.  
            {
  27.  
                    // 首先检测等号右边的是否就是左边的对象本身,若是本对象本身,则直接返回
  28.  
                    if ( this == &rhs ) 
  29.  
                    {
  30.  
                            return *this;
  31.  
                    }                
  32.  
                    // 复制等号右边的成员到左边的对象中
  33.  
                    this->m_real = rhs.m_real;
  34.  
                    this->m_imag = rhs.m_imag;                
  35.  
                   // 把等号左边的对象再次传出            
  36.  
                    return *this;
  37.  
            }
  38.  
    };

//    无参数构造函数
        // 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做
        // 只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来
       
        //    一般构造函数(也称重载构造函数)
        // 一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)
        // 例如:你还可以写一个 Complex( int num)的构造函数出来
        // 创建对象时根据传入的参数不同调用不同的构造函数
       
        //    复制构造函数(也称为拷贝构造函数)
        //    复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中
        //    若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询 有关 “浅拷贝” 、“深拷贝”的文章论述
           
        // 类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象,
     //需要注意的一点是,这个其实就是一般的构造函数,但是对于出现这种单参数的构造函数,C++会默认将参数对应的类型转换为该类类型,有时候这种隐私的转换是我们所不想要的,所以需要使用explicit来限制这种转换。

// 赋值构造函数
        // 注意,这个类似复制构造函数,将=右边的本类对象的值复制给等号左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建
        // 若没有显示的写=运算符重载,则系统也会创建一个默认的=运算符重载,只做一些基本的拷贝工作
       

下面使用上面定义的类对象来说明各个构造函数的用法:
int main()
{
        // 调用了无参构造函数,数据成员初值被赋为0.0
        Complex c1,c2;

        // 调用一般构造函数,数据成员初值被赋为指定值
        Complex c3(1.0,2.5);
        // 也可以使用下面的形式
        Complex c3 = Complex(1.0,2.5);
        
        //    把c3的数据成员的值赋值给c1
        //    由于c1已经事先被创建,故此处不会调用任何构造函数
        //    只会调用 = 号运算符重载函数
        c1 = c3;        
             
       // 调用拷贝构造函数( 有下面两种调用方式) 
        Complex c5(c2);
        Complex c4 = c2;  // 注意和 = 运算符重载区分,这里等号左边的对象不是事先已经创建,故需要调用拷贝构造函数,参数为c2
//这一点特别重要,这儿是初始化,不是赋值。其实这儿就涉及了C++中的两种初始化的方式:复制初始化和赋值初始化。其中c5采用的是复制初始化,而c4采用的是赋值初始化,这两种方式都是要调用拷贝构造函数的。
}

三、深拷贝与浅拷贝

如果没有自定义复制构造函数,则系统会创建默认的复制构造函数,但系统创建的默认复制构造函数只会执行“浅拷贝”,即将被拷贝对象的数据成员的值一一赋值给新创建的对象,若该类的数据成员中有指针成员,则会使得新的对象的指针所指向的地址与被拷贝对象的指针所指向的地址相同,delete该指针 时则会导致两次重复delete而出错。下面是示例:

  1.  
    #include <iostream.h>
  2.  
    #include <string.h>
  3.  
    class Person 
  4.  
    {
  5.  
    public :
  6.  
             // 构造函数
  7.  
            Person(char * pN)
  8.  
            {
  9.  
                  m_pName = new char[strlen(pN) + 1];
  10.  
                  //在堆中开辟一个内存块存放pN所指的字符串
  11.  
                  if(m_pName != NULL) 
  12.  
                  {
  13.  
                     //如果m_pName不是空指针,则把形参指针pN所指的字符串复制给它
  14.  
                       strcpy(m_pName ,pN);
  15.  
                  }
  16.  
            }        
  17.  
            
  18.  
            // 系统创建的默认复制构造函数,只做位模式拷贝
  19.  
            Person(Person & p)    
  20.  
            { 
  21.  
                      //使两个字符串指针指向同一地址位置         
  22.  
                     m_pName = p.m_pName;         
  23.  
            }
  24.  
             ~Person( )
  25.  
            {
  26.  
                    delete m_pName;
  27.  
            }
  28.  
      private :
  29.  
     
  30.  
            char * m_pName;
  31.  
    };
  32.  
     
  33.  
    void main( )
  34.  
  35.  
            Person man("lujun");
  36.  
            Person woman(man); 
  37.  
            
  38.  
            // 结果导致   man 和    woman 的指针都指向了同一个地址
  39.  
            
  40.  
            // 函数结束析构时
  41.  
            // 同一个地址被delete两次
  42.  
    }

// 下面自己设计复制构造函数,实现“深拷贝”,即不让指针指向同一地址,而是重新申请一块内存给新的对象的指针数据成员
Person(Person & chs);
{
         // 用运算符new为新对象的指针数据成员分配空间
         m_pName=new char[strlen(p.m_pName)+ 1];

         if(m_pName)         
         {
                 // 复制内容
                strcpy(m_pName ,chs.m_pName);
         }
      
        // 则新创建的对象的m_pName与原对象chs的m_pName不再指向同一地址了
}

四、构造函数显示调用和隐式调用

C/C++中的显示调用和隐式调用:

(1)显示调用

显示调用是指在程序中能找到相应的调用代码,或者说是手动调用的

(2)隐式调用

隐式调用是指程序中找不到相应的调用代码,或者说是编译器自动调用的

类的构造函数与析构函数一般就是隐式调用的。

如下代码:

  1.  
    A()
  2.  
    {
  3.  
    //一般构造函数
  4.  
    }
  5.  
     
  6.  
    A& operator =(const A& a)
  7.  
    {
  8.  
    // 赋值构造函数
  9.  
    }
  10.  
     
  11.  
    A(const A& a)
  12.  
    {
  13.  
    //拷贝构造函数
  14.  
    }
  15.  
     
  16.  
    ~A()
  17.  
    {
  18.  
    }

下面给出一些构造示例:

  1.  
    void f()
  2.  
    {
  3.  
    // A()构造函数被调用
  4.  
    A a;
  5.  
    // A(const A& a)构造函数被调用
  6.  
    A b(a);
  7.  
    // A(const A& a)构造函数被调用
  8.  
    A c = a;
  9.  
    // A& operator = (const A& a)赋值操作符重载函数被调用
  10.  
    b = c;
  11.  
    }
  12.  
     
  13.  
    // 离开f()函数之前,a,b,c的析构函数被调用,做一些清理工作

注意上面 "A c = a" ,这句代码实际调用的是拷贝构造函数,而非赋值函数。属于构造函数的隐式调用,可以使用explicit修饰构造函数的定义,使得其不能被隐式调用,如下:

    1.  
      explicit A(int n)
    2.  
      {
    3.  
      m_data = NULL;
    4.  
      if (n>0)
    5.  
      m_data = new int[n];
    6.  
      }

C++类构造函数、拷贝构造函数、复制构造函数、复制构造函数、构造函数显示调用和隐式调用的更多相关文章

  1. C++转换构造函数和隐式转换函数 ~ 转载

    原文地址: C++转换构造函数和隐式转换函数 用转换构造函数可以将一个指定类型的数据转换为类的对象.但是不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个Complex类对象转换成doubl ...

  2. C++ 隐式类类型转换

    <C++ Primer>中提到: “可以用 单个形参来调用 的构造函数定义了从 形参类型 到 该类类型 的一个隐式转换.” 这里应该注意的是, “可以用单个形参进行调用” 并不是指构造函数 ...

  3. C++ 隐式类类型转换和转换操作符

    隐式类类型转换 C++语言定义了内置类型之间的几个自动转换.也可以定义如何将其他类型的对象隐式转换为我们的类类型,或将我们的类类型的对象隐式转换为其他类型.为了定义到类类型的隐式转换,需要定义合适的构 ...

  4. C++隐式类类型转化

    隐式类类型转换:可以用 单个形参来调用 的构造函数定义了从 形参类型 到 该类类型 的一个隐式转换 class Person { public: Person(): mName()name, mAge ...

  5. [C++]复制构造函数、赋值操作符与隐式类类型转换

    问题:现有类A定义如下: class A{public:        A(int a)                            //构造函数        {              ...

  6. C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数

    构造函数 ,是一种特殊的方法 .主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 .特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数 ...

  7. C++编写字符串类CNString,该类有默认构造函数、类的拷贝函数、类的析构函数及运算符重载

    编码实现字符串类CNString,该类有默认构造函数.类的拷贝函数.类的析构函数及运算符重载,需实现以下“=”运算符.“+”运算.“[]”运算符.“<”运算符及“>”运算符及“==”运算符 ...

  8. C++类中拷贝构造函数详解

    a. C++标准中提到"The default constructor, copy constructor and copy assignment operator, and destruc ...

  9. C++ //构造函数调用规则 //1.创建一个类,C++编译器会给每个类添加至少3个函数 //默认构造(空实现) //析构函数(空实现) //拷贝函数(值拷贝) //2.如果我们写了有参构造函数 编译器就不会提供默认构造函数 但是会提供拷贝构造函数 //3.如果我们写了拷贝函数 编译器就不再提供 默认 有参 构造函数

    //构造函数调用规则 #include <iostream> using namespace std; //1.创建一个类,C++编译器会给每个类添加至少3个函数 //默认构造(空实现) ...

随机推荐

  1. Kubernetes全栈架构师(二进制高可用安装k8s集群扩展篇)--学习笔记

    目录 二进制Metrics&Dashboard安装 二进制高可用集群可用性验证 生产环境k8s集群关键性配置 Bootstrapping: Kubelet启动过程 Bootstrapping: ...

  2. Python中字典get方法的使用技巧

    get方法,用于获取字典中某个键值key 对应value的值,此方法可以接收两个参数,第一个参数传入key的值,第二个参数用于传入一个自定义返回值,如果查询的key在字典中存在,就会反回对应key在字 ...

  3. Pytest单元测试框架之setup/teardown模块示例操作

    """模块级(setup_module/teardown_module)开始于模块始末,全局的函数级(setup_function/teardown_function)只 ...

  4. Optional 的使用会导致性能下降吗?

    几天前,我在论坛上发了一篇关于Optional 的文章.其中一条评论是一个非常好的问题: Optional 的使用会导致性能下降吗? 答案是: 是的,它会的.但是你应该担心吗? 使用Optional的 ...

  5. 线性回归与梯度下降(ML作业)

    Loss函数 题目一:完成computeCost.m function J = computeCost(X, y, theta) %COMPUTECOST Compute cost for linea ...

  6. java 8新特性 并行流

    使用并行流,提高cpu利用率,提高运算速度 /** * java 8并行流 * 底层运用fork join框架 */ @Test public void test(){ Instant start = ...

  7. [考试总结]noip18

    发现之前咕掉了这个考试的总结. 今天就把它给补上. 这也是一个炸裂的一场 开局以为 \(T1\) 可做,然而事实证明我又错了... 莽了一个随机化上去,轻松过了所有样例... 以为稳了 然而挂掉了.. ...

  8. 通过Xlib枚举指定进程下所有窗体

    在windows系统下如果想要枚举指定进程的窗体,我们可以通过EnumWindows加上自己实现的回调函数进行实现,那么在linux下该如何做呢? 其实也很简单,在linux下,我们可以通过xlib中 ...

  9. django2.1实现全文检索(最详细)+遇到的坑+jieba分词

    首先django实现全文检索在这里使用的是haystack,环境是django2.1+win10 64+py3.7 1: 安装包: pip install dgango-haystack #安装全局检 ...

  10. SQL SERVER 雨量计累计雨量(小时)的统计思路

    PLC中定时读取5分钟雨量值,如何将该值统计为小时雨量作为累计?在sql server group by聚合函数,轻松实现该目的. 1.编写思路 数据库中字段依据datetime每五分钟插入一条语句, ...