一、 构造函数是干什么的

  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. python使用笔记11--时间模块

    1.时间模块常用方法 1 import time,datetime 2 #格式化好的时间2020-05-16 18:30:52 3 #时间戳1589616753 从unix元年(计算机发明的时间)到现 ...

  2. 【动画消消乐】HTML+CSS 自定义加载动画 061

    前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 自我介绍ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计算机专 ...

  3. java网络编程基础——TCP网络编程三

    AIO实现非阻塞通信 java7 NIO2 提供了异步Channel支持,这种异步Channel可以提供更高效的IO,这种基于异步Channel的IO被称为异步IO(Asynchronous IO) ...

  4. IDEA搭建springmvc环境之后无故出现404

    其实这应该是IDEA的自身问题 打开项目结构

  5. debian 9 pycharm安装

    官网下载PyCharm的tar.gz格式 使用命令进行解压:tar -xvzf pycharm.tar.gz 解压后将pycharm文件夹移动到/usr/local/lib/目录下 进入pycharm ...

  6. 关键字替代符号C++

    不知从什么时候的哪里我看到了一个and关键字,心想这是个什么玩意...然后知道它相当于&&,于是开启了罪恶的生涯-- 替代关键字,似乎可读性更好,但是有些编译器可能会无法识别(eg.M ...

  7. element UI+vue关于日期范围选择的操作,picker-options属性的使用

    一般 <el-date-picker />使用会出现起始日期和结束日期,结束日期不能早与起始日期,选择了其实日期后,结束日期大于起始日期的不可选,置灰,同理先选结束日期后再选起始日期,那么 ...

  8. 利用 cgroup 的 cpuset 控制器限制进程的 CPU 使用

    最近在做一些性能测试的事情,首要前提是控制住 CPU 的使用量.最直观的方法无疑是安装 Docker,在每个配置了参数的容器里运行基准程序. 对于计算密集型任务,在只限制 CPU 的需求下,直接用 L ...

  9. netty系列之:Event、Handler和Pipeline

    目录 简介 ChannelPipeline ChannelHandler ChannelHandlerContext ChannelHandler中的状态变量 异步Handler 总结 简介 上一节我 ...

  10. 安卓安装https证书

    前置条件 1 手机要设置密码 然后安装charles 证书 2 赋予 adb shell root权限(安装magisk就行) adb shell # 连接手机进入shell模式 su root # ...