一、 构造函数是干什么的

  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. C++ 标准模板库(STL)——容器(Containers)的用法及理解

    C++ 标准模板库(STL)中定义了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量(vector).队列(queue).栈(stack).set.map等.这次主要 ...

  2. springMVC-2-MVC初步了解

    Spring MVC的特点 轻量级,简单易学 高效 , 基于请求响应的MVC框架 与Spring兼容性好,无缝结合 约定优于配置 功能强大:RESTful.数据验证.格式化.本地化.主题等 简洁灵活 ...

  3. 小程序框架WePY 从入门到放弃踩坑合集

    小程序框架WePY 从入门到放弃踩坑合集 一点点介绍WePY 因为小程序的语法设计略迷, 所以x1 模块化起来并不方便, 所以x2 各厂就出了不少的框架用以方便小程序的开发, 腾讯看到别人家都出了框架 ...

  4. Scrapy 爬虫框架学习笔记(未完,持续更新)

    Scrapy 爬虫框架 Scrapy 是一个用 Python 写的 Crawler Framework .它使用 Twisted 这个异步网络库来处理网络通信. Scrapy 框架的主要架构 根据它官 ...

  5. PAT乙级:1057 数零壹 (20分)

    PAT乙级:1057 数零壹 (20分) 题干 给定一串长度不超过 105 的字符串,本题要求你将其中所有英文字母的序号(字母 a-z 对应序号 1-26,不分大小写)相加,得到整数 N,然后再分析一 ...

  6. PAT乙级:1056 组合数的和 (15分)

    PAT乙级:1056 组合数的和 (15分) 给定 N 个非 0 的个位数字,用其中任意 2 个数字都可以组合成 1 个 2 位的数字.要求所有可能组合出来的 2 位数字的和.例如给定 2.5.8,则 ...

  7. 优化 Workerman 检查主进程是否存活的逻辑

    主要新增了判断进程是否为 Workerman 进程的逻辑,从而优化了确定主进程是否存活的准确性 发现问题 年前逛 GitHub 的时候,发现 Workerman 有一个 2017 年打开的 Issue ...

  8. debian 9安装细节

    1.安装KDE桌面 2.开机桌面正常启动,首先在grub启动界面,按"e"键,在linux......quiet后面加上nomodeset,然后进入桌面,在终端输入: su -vi ...

  9. SQlL 中 where 1=1

    提升某种执行效率? 其实,1=1 是永恒成立的,意思无条件的,也就是说在SQL语句中有没有这个1=1都可以. 这个1=1常用于应用程序根据用户选择项的不同拼凑where条件时用的. 如:web界面查询 ...

  10. js 正序、倒序、按字段排序方法

    js 基础--sort方法: arrayObject.sort(sortby); 参数:定义排序规则(正序.倒序.按字段排序)的函数: 返回值:对数组的引用.请注意,数组在原数组上进行排序,不生成副本 ...