构造函数

一、 构造函数是干什么的

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

构造函数的几个特点:

   1.函数名和类名必须一样,没有返回值。2.当没有显式的定义构造函数时,系统会自己生成默认的构造函数。3.构造函数可以重载。
class Stack
{public:// 构造函数类名为Stack// 特点:以类名作为函数名,无返回类型Stack(){value = 0;}
private:// 数据成员int value;
}

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

二、 构造函数的种类

class Complex
{
private :double    m_real;double    m_imag;
public://    无参数构造函数// 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做// 只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来Complex(void){m_real = 0.0;m_imag = 0.0;}   //    一般构造函数(也称重载构造函数)// 一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)// 例如:你还可以写一个 Complex( int num)的构造函数出来// 创建对象时根据传入的参数不同调用不同的构造函数Complex(double real, double imag){m_real = real;m_imag = imag;         }  //    复制构造函数(也称为拷贝构造函数)//    复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中//    若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询 有关 “浅拷贝” 、“深拷贝”的文章论述Complex(const Complex & c){// 将对象c中的数据成员值复制过来m_real = c.m_real;m_imag    = c.m_imag;}            // 类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象,//需要注意的一点是,这个其实就是一般的构造函数,但是对于出现这种单参数的构造函数,C++会默认将参数对应的类型转换为该类类型,有时候这种隐私的转换是我们所不想要的,所以需要使用explicit来限制这种转换。// 例如:下面将根据一个double类型的对象创建了一个Complex对象Complex(double r){m_real = r;m_imag = 0.0;}// 等号运算符重载(也叫赋值构造函数)// 注意,这个类似复制构造函数,将=右边的本类对象的值复制给等号左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建// 若没有显示的写=运算符重载,则系统也会创建一个默认的=运算符重载,只做一些基本的拷贝工作Complex &operator=( const Complex &rhs ){// 首先检测等号右边的是否就是左边的对象本身,若是本对象本身,则直接返回if ( this == &rhs ) {return *this;}                // 复制等号右边的成员到左边的对象中this->m_real = rhs.m_real;this->m_imag = rhs.m_imag;                // 把等号左边的对象再次传出// 目的是为了支持连等 eg:    a=b=c 系统首先运行 b=c// 然后运行 a= ( b=c的返回值,这里应该是复制c值后的b对象)    return *this;}
};

下面使用上面定义的类对象来说明各个构造函数的用法:

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

参考:http://www.cnblogs.com/xkfz007/archive/2012/05/11/2496447.html

析构函数

析构函数(destructor)是成员函数的一种,它的名字与类名相同,但前面要加~,没有参数和返回值。一个类有且仅有一个析构函数。如果定义类时没写析构函数,则编译器生成默认析构函数。如果定义了析构函数,则编译器不生成默认析构函数。析构函数在对象消亡时即自动被调用。可以定义析构函数在对象消亡前做善后工作。例如,对象如果在生存期间用 new 运算符动态分配了内存,则在各处写 delete 语句以确保程序的每条执行路径都能释放这片内存是比较麻烦的事情。有了析构函数,只要在析构函数中调用 delete 语句,就能确保对象运行中用 new 运算符分配的空间在对象消亡时被释放。

和构造函数一样,析构函数名称也很特殊:在类名前面加上~。因此,Stack类的析构函数为 ~Stack。另外,和构造函数一样,析构函数也没有返回值和声明类型。
与构造函数不同的是,析构函数没有参数,因此Stack析构函数原型必须是这样的: ~Stack();由于Stack的析构函数不承担任何重要的工作,因此可以将它编写为不执行任何操作的函数:

Stack::~Stack()
{}

然而,为了能看出析构函数在何时被调用,可以这样编写其代码:

Stack::~Stack()
{cout << "析构函数!\n" << endl;
}

this指针

this 是 C++ 中的一个关键字,也是一个 const 指针,它指向当前对象,通过它可以访问当前对象的所有成员。

所谓当前对象,是指正在使用的对象。例如对于stu.show();,stu 就是当前对象,this 就指向 stu。

下面是使用 this 的一个完整示例:

#include <iostream>
using namespace std;
class Student{public:void setname(char *name);void setage(int age);void setscore(float score);void show();
private:char *name;int age;float score;
};
void Student::setname(char *name){this->name = name;
}
void Student::setage(int age){this->age = age;
}
void Student::setscore(float score){this->score = score;
}
void Student::show(){cout<<this->name<<"的年龄是"<<this->age<<",成绩是"<<this->score<<endl;
}
int main(){Student *pstu = new Student;pstu -> setname("李华");pstu -> setage(16);pstu -> setscore(96.5);pstu -> show();return 0;
}

运行结果:
李华的年龄是16,成绩是96.5

this 只能用在类的内部,通过 this 可以访问类的所有成员,包括 private、protected、public 属性的。

本例中成员函数的参数和成员变量重名,只能通过 this 区分。以成员函数setname(char *name)为例,它的形参是name,和成员变量name重名,如果写作name = name;这样的语句,就是给形参name赋值,而不是给成员变量name赋值。而写作this -> name = name;后,=左边的name就是成员变量,右边的name就是形参,一目了然。

注意,this 是一个指针,要用->来访问成员变量或成员函数。

this 虽然用在类的内部,但是只有在对象被创建以后才会给 this 赋值,并且这个赋值的过程是编译器自动完成的,不需要用户干预,用户也不能显式地给 this 赋值。本例中,this 的值和 pstu 的值是相同的。

我们不妨来证明一下,给 Student 类添加一个成员函数printThis(),专门用来输出 this 的值,如下所示:

void Student::printThis(){cout<<this<<endl;
}

然后在 main() 函数中创建对象并调用 printThis():

Student *pstu1 = new Student;
pstu1 -> printThis();
cout<<pstu1<<endl;
Student *pstu2 = new Student;
pstu2 -> printThis();
cout<<pstu2<<endl;

运行结果:
0x7b17d8
0x7b17d8
0x7b17f0
0x7b17f0

可以发现,this 确实指向了当前对象,而且对于不同的对象,this 的值也不一样。

几点注意:
this 是 const 指针,它的值是不能被修改的,一切企图修改该指针的操作,如赋值、递增、递减等都是不允许的。
this 只能在成员函数内部使用,用在其他地方没有意义,也是非法的。
只有当对象被创建后 this 才有意义,因此不能在 static 成员函数中使用(后续会讲到 static 成员)。
this 到底是什么
this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给 this。不过 this 这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默地将它添加到参数列表中。

this 作为隐式形参,本质上是成员函数的局部变量,所以只能用在成员函数的内部,并且只有在通过对象调用成员函数时才给 this 赋值。

在《C++函数编译原理和成员函数的实现》一节中讲到,成员函数最终被编译成与对象无关的普通函数,除了成员变量,会丢失所有信息,所以编译时要在成员函数中添加一个额外的参数,把当前对象的首地址传入,以此来关联成员函数和成员变量。这个额外的参数,实际上就是 this,它是成员函数和成员变量关联的桥梁。

初始化列表

定义

与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。

class foo
{public:
foo(string s, int i):name(s), id(i){} ; // 初始化列表
private:
string name ;int id ;
};

使用初始化列表的原因

初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作。
主要是性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?由下面的测试可知,使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。

必须使用初始化列表的时候

除了性能问题之外,有些时候合初始化列表是不可或缺的,以下几种情况时必须使用初始化列表
1.常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
2.引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
3. 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化

成员变量的顺序
成员是按照他们在类中出现的顺序进行初始化的,而不是按照他们在初始化列表出现的顺序初始化的,看代码:

class foo
{public:
int i ;int j ;
foo(int x):i(x), j(i){}; // ok, 先初始化i,后初始化j
};

再看下面的代码:

class foo
{public:
int i ;int j ;
foo(int x):j(x), i(j){} // i值未定义
};

这里i的值是未定义的因为虽然j在初始化列表里面出现在i前面,但是i先于j定义,所以先初始化i,而i由j初始化,此时j尚未初始化,所以导致i的值未定义。一个好的习惯是,按照成员定义的顺序进行初始化。

C++构造函数、析构函数、this指针、初始化列表相关推荐

  1. C++ 构造函数体内赋值与初始化列表的区别

    Linux 环境下,使用 g++ 编译以下使用初始化列表的代码时出现编译错误error: expected '{' before 'this'. class someClass {int num;st ...

  2. C++ 构造函数的初始化列表

    (1)如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,没有默认构造函数.这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如果没有初始化 ...

  3. C++ 类(构造函数的成员初始化列表)

    文章概述 构造函数的成员初始化列表 构造函数的成员初始化列表 下面的代码分析了什么时候使用成员初始化列表: class A {private:int a;public:A(int a){this-&g ...

  4. C++ 构造函数初始化列表

    <C++ Primer>中提到在以下三种情况下需要使用构造函数初始化列表: 需要初始化的类的成员变量是对象的情况: 需要初始化的类的成员变量由const修饰的或初始化的类的引用成员变量: ...

  5. 28.构造函数中,成员变量一定要通过初始化列表来初始化的?

    首先要明确:如果对象成员是const或者引用的话,必须将其初始化! 构造函数中,成员变量一定要通过初始化列表来初始化的的几种情况! 1)对象成员是const或者引用 #include <iost ...

  6. C++:构造函数之初始化列表

    初始化列表有什么用? 在不知道初始化列表的时候我可以在构造函数体内进行初始化,就是对成员变量赋值.为什么还需要初始化列表? 这是因为,有些类型的数据无法通过在构造函数体内进行赋值来进行初始化.这样的数 ...

  7. 初始化列表的使用(十五)

    我们之前在 C 语言中可以定义 const 成员,那么我们是否可以在类中定义 const 成员呢?我们来看看下面代码中的类定义是否合法呢?如果合法,ci 的值是什么,存储在哪里呢? #include ...

  8. 【C++】-- 初始化列表

    目录 一.用初始化列表初始化对象 1.初始化列表用法 2.初始化列表特性 二.explicit关键字 1.内置类型的隐式转换 2.如何避免单参构造函数初始化发生隐式类型转换 创建一个类对象时,编译器通 ...

  9. C++类构造函数中的成员初始化

    成员初始化列表,形式如下(在参数列表后面以冒号为起点,之后是一个初始化列表): Rectangle::Rectangle (int x, int y) : width(x), height(y) { ...

  10. [C++11]统一的数据初始化方式 - 初始化列表

    关于C++中的变量,数组,对象等都有不同的初始化方法,在这些繁琐的初始化方法中没有任何一种方式适用于所有的情况.为了统一初始化方式,并且让初始化行为具有确定的效果,在C++11中提出了列表初始化的概念 ...

最新文章

  1. Day2 : iOS第三方框架MBProgressHUD学习笔记
  2. PostgreSQL 务实应用(三/5)分表复制
  3. BZOJ 4706: B君的多边形 找规律
  4. clickhouse的常见问题以及和mysql相关特性对比
  5. Linux资源控制-CPU和内存【转】
  6. 英语发音规则---A字母
  7. K8s(二):130 道 K8s/Docker 配套练习题,学+练结合,一次吃透
  8. (转)两大量化交易巨头“抱团取暖”:Virtu14亿美元收购骑士资本
  9. Excel文件解密软件
  10. 标签类目体系(面向业务的数据资产设计方法论)-读书笔记5
  11. php加入语音播报功能_微信收付款怎么设置语音播报
  12. java分布式事务框架_Java分布式事务,及解决方案
  13. 判断质数和合数python代码_【奇技淫巧】利用正则进行需要整除操作的判断,如:奇偶性,质数合数...
  14. 解决PHP提示Warning: Division by zero in错误
  15. 点击地图出现导航php,在H5页面中,通过地址打开高德地图以实现导航
  16. TP6------图片上传/多图上传
  17. 如何彻底删除小黑记事本弹窗
  18. Workfine5.0扩展功能——如何识别身份证信息?
  19. NFT 制作生成进阶:男女性别区分+特殊款形象/头像完整项目
  20. 从永远到永远-Navicat将MySQL数据库复制到另一个Mysql数据库

热门文章

  1. opencv——图形金字塔、轮廓、模板
  2. 中国石油大学《人力资源开发与管理》第二次在线作业
  3. 【我的OpenGL学习进阶之旅】OpenGL ES 3.0新功能
  4. 工业物联网 SCA-IOT2050快速收集三菱CNC数控系统数据至OPC UA服务器
  5. 电子计算机制造业行业前景,电子计算机行业发展趋势如何 2021电子计算机行业现状及发展前景分析...
  6. filezilla传输文件给Linux显示传输文件错误解决方法
  7. 井通科技 智能合约文档
  8. 2021-10-24求救大神计算机思维
  9. CAN总线对比485总线
  10. Windows系统如何设置电脑内网连接网线,外网连接wifi