近来回转 C++的学习,脑子又被搞得了一团迷简直不要忘得太快.....   

过后静下来想一想,还是因为有些东西没搞清楚导致,所以理了理两个容易搞迷糊的地方。

引用与指针


C++进行传值更倾向于使用引用。引用实质就是给已经定义的变量起一个别名,函数通过这个别名来完成对应的功能。

【引用特点】

  ①一变量可取多个别名

  ②引用必须初始化,同时只能在初始化时被引用,

  ③只能被引用同一变量,从一而终

【使用引用注意几种情况】

(1)何时添加const进行修饰

①防止引用变量被修改

我们知道在变量前加const 表示这是个常变量,不能被修改。那么在引用前加上const是一样的道理,例如: int a = 2; const int& d = a; 这样的形式防止变量a的别名d 对值‘2’进行修改

②引用的为常量   如:

常量是具有常性的,所以必须在前面加上const使其保持常性。

③引用参数存在隐式类型转换

(2)函数传引用作返回值

①不要返回临时变量的引用

例如

int& Add(int d1, int d2)  //临时变量的引用作返回值
{
int ret = d1 + d2;
return ret;
}
void Test()
{
int& sum = Add(, ); //获取返回值ret的别名
cout<<"占用位置"<<endl;
cout<<sum<<endl;
}

结果

分析:ret是隶属于Add函数栈帧,ret的引用作返回值,返回的其实是ret变量的地址;而当Add函数调用完毕后,该处被操作系统收回权限,若再通过返回的地址访问该处就是非法的(结果便成了上图随机值)。这与传值返回有着很大的差别,

(传值返回&引用返回 汇编代码)

②当返回的对象出了函数作用域依旧存在,最好使用引用作返回,因为它更高效。

  因为引用返回仅仅是一个别名(其实是保存在寄存器eax中的地址),而若是传值返回,且返回的ret是一个对象,便会产生临时对象,这个临时对象用ret拷贝构造初始化(拷贝构造请往下看,而这个临时对象底层是在返回值接收方的函数中提前开辟好的,在函数接受方接收完成后,还需调用析构函数来清理该临时对象,进一步增大了开销;所以用引用返回会更高效。

:返回值优化参考http://www.cnblogs.com/hazir/archive/2012/04/19/2456840.html

然后就是注意它和指针的区别(比较重要)。

引用和指针区别

  *  引用只能在定义时初始化一次,之后不能改变去指向其它变量(从一而终);指针变量的值可变
  *  引用必须指向有效的变量,指针可以为空;用指针传参,函数栈额外开空间来拷贝一份参数地址,引用传参则不会。
  * sizeof指针对象和引用对象的意义不一样。sizeof引用得到的是所指向的变量的大小,而sizeof指针是对象地址的大小。
  * 指针和引用自增(++)自减(--)意义不一样。

 

 总之, 相对而言,引用比指针更安全,指针更灵活。

构造函数


首先,它是用来初始化类里面的成员变量的公有成员函数,有且仅在定义对象时自动执行一次。

它有如下基本特征:

  1. 函数名与类名相同。
  2. 无返回值。
  3. 对象实例化时系统自动调用对应的构造函数。
  4. 构造函数可以重载。
  5. 构造函数可以在类中实现,也可以在类外实现(类外加上类限定符)

  6. 如果类定义中没有给出构造函数,则C++编译器自动产生一个缺省的构造函数,但只要我们定义了一个构造函数,系统就不会自动生成缺省的构造函数。(半缺省时,只能从最右边往左连续着缺省,如:Date(int year, int month =1, int year = 1 ),这和参数入栈规则有关)

  7. 无参的构造函数和全缺省值的构造函数都认为是缺省构造函数,并且缺省的构造函数只能有一个。

【构造函数初始化列表】

  我们一般都是在构造函数体内来初始化数据成员,但这并不是真正意义上的初始化,而是赋值;初始化发生时间更早,是在数据成员的defalut(缺省)构造函数被自动调用的时候,内置类型除外。defalut构造函数先为这些数据成员设初值,然后自定义实现的构造函数再对它们赋予新值。于是这样便把default构造函数的一切作为给白白浪费了,效率有待提高。

  所以一般初始化是使用“初始化列表”来进行的。初始化列表以一个冒号开始,接着一个逗号分隔数据列表,每个数据成员都放在一个括号中进行初始化。如下:

class B(int b1, int b2)
: _b1(b1)
, _b2(b2)
{//可以保留原构造赋值部分} private:
int _b1;
int _b2;
};

初始化列表

它位于构造函数参数列表后,在函数体{}之前,这说明该列表里的初始化工作发生在函数体内的任何代码被执行之前。该列表中针对各个成员变量而设的参数,直接当实参被拿去作初值(或传进另一拷贝构造函数中来初始化该成员变量),这样也就避免前面提到的“白白浪费”了,这也正是 effectiveC++所提到的观点。

  所以尽量使用初始化列表进行初始化,因为它是更高效的。

大部分时候构造函数既可以使用初始化列表又能在函数体内初始化,但有几种情况必须使用初始化列表

  • 类的非静态的const 数据成员 (成员被const修饰,于是被定义出来之后,构造函数来初始化,此时已不能被修改)
  • 为引用的数据成员  (引用从一而终)
  • 没有缺省构造函数的类的成员变量  ()

注意!!

    成员变量按声明顺序依次初始化,而非初始化列表出现的顺序  (看如下例子)

class A
{
public:
A(int n)
: _a2(n)
, _a1(_a2)
{} void Show()
{
cout<<"a1 "<<_a1<<endl<<"a2 "<<_a2<<endl;
}
private:
int _a1;
int _a2;
}; int main()
{
A a();
a.Show();
return ;
}

不难看出在构造函数中是先跟变量_a2赋值,然后再用_a2来初始化_a1,结果就应该是 1 和 1;但是结果却是

因为成员变量按声明顺序依次初始化,而非初始化列表出现的顺序。这里按变量定义顺序,应该先初始化 _a1 ,再初始化 _a2 ,但初始化列表里, _a1 是使用 _a2 的值来初始化的,但此时 _a2还没有被初始化,于是_a1就不会被初始化了。

接下来说说一个特殊的构造函数

【拷贝构造函数】

创建对象时使用同类对象来进行初始化(相当于复制对象),这时所用的构造函数称为拷贝构造函数(Copy Constructor),拷贝构造函数是特殊的构造函数。

class Date
{
public :
Date()
{}
// 拷贝构造函数
Date (const Date& d)
{
_year = d ._year;
_month = d ._month;
_day = d ._day;
}
private :
int _year ;
int _month ;
int _day ;
};

拷贝构造实现

特征:
  1. 拷贝构造函数其实是一个构造函数的重载。
  2. 拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用(调拷贝构造函数会传参,传参过程又会调用拷贝构造,以此往复...无穷递归)
  3. 若未显示定义,系统会生成默认缺省的拷贝构造函数。缺省的拷贝构造函数会依次拷贝类成员进行初始化

何时调用拷贝函数:

  1. 一个对象以值传递的方式传入函数体
  2. 一个对象以值传递的方式从函数返回(与返回值优化密切相关)
  3. 一个对象需要通过另一个对象进行初始化

关于拷贝构造函数还有一类很热的问题,构造函数拷贝赋值函数的N种调用情况

即判断下面每种情况都调用了多少次构造、拷贝构造...

//1.Date 对象做参数传值
void fun1 (Date d) //void fun1(Date& d)
{}
// 2.Date 对象做返回值传值
Date fun2 () // Date& fun2()
{
Date d ;
return d ;
}
// 3.Date 对象做临时返回值传值 (编译器优化问题)
Date fun3 () // Date& fun3()
{
return Date ();
}
int main ()
{
// 场景
Date d1;
fun1(d1);
// 场景
Date d2 = fun2();
// 场景
Date d3 ;
d3 = fun3 ();
return ;
}

这其实和前面所说的传值返回也有着紧密的联系,同时还涉及编译器的优化,如果有兴趣可以参考:

http://www.cnblogs.com/hazir/archive/2012/04/19/2456840.html

C++引用指针 & 构造函数的更多相关文章

  1. C++ const 引用 指针

    先简单回忆一下常量的性质: int main() { const int buffSize = 512; buffsize = 512; //× buffSize是常量 } 初始化时: const i ...

  2. perl5 第十二章 Perl5中的引用/指针

    第十二章 Perl5中的引用/指针 by flamephoenix 一.引用简介二.使用引用三.使用反斜线(\)操作符四.引用和数组五.多维数组六.子程序的引用  子程序模板七.数组与子程序八.文件句 ...

  3. 引用&指针交换函数实践

    实践如下: #include <iostream> using namespace std; // 普通交换,注意这里的ab值,在具体调用时是基本数据的拷贝,原始数据不会变化 // 因此这 ...

  4. C++ 引用、构造函数、移动语义

    1.引用 C++中的引用主要用作函数的形参,接近于const指针,必须在创建时初始化. 以Person类为例,如下: Person p;                          //调用P的 ...

  5. c/c++ 函数指针 指针函数 数组的引用 指针数组 数组指针

    1.指针数组数组指针 引用数组 数组的引用 int *a[10] 指针数组 每一个元素都是一个指针 Int (*a)[10] 数组指针 P指向一个含有10个元素的数组 Int (&a)[10] ...

  6. C++ Primer 第二章 引用 指针 const限定符

    1.引用: 为对象起了另外一个名字,引用类型引用另外一种类型,通过将声明符写成&d的形式来定义引用类型,其中d也就是声明的变量名(声明符就是变量名). PS:1.通过图片中编译所提示的报错信息 ...

  7. 子类父类(虚函数下的 引用指针 对象)->看来没有子类指针这回事

    #include<iostream> using namespace std; class Father { public: Father() { cout << " ...

  8. python3.x 类似cpp引用指针传参修改

    #同名局部变量调用外部全局变量: num=100def fun(): global num#告诉编译器是全局的num num+=100 print(num)print(fun)print(fun()) ...

  9. C++ 引用 指针 使用举例

    1. 请看下程序 inline void CScanLineFill::removeOldNodeAET(AET* &aetList, const float yCurrent) { AET* ...

随机推荐

  1. AVS、MPEG-2、H264标准文档

    联合信源对AVS解码源码和相应的AVS码流.AVS码流太大,可以从http://cosoft.org.cn/projects/avsdec下载.解压avsdec_source.zip后,用VC6编译a ...

  2. ARM开发软件ADS教程

    ARM开发软件ADS教程 ADS(ARM Developer Suite)是ARM公司推出ARM集成开发环境,操作简单方便,获得广大开发人员的青睐.下面使用ADS v1.2做一个实例教程,帮助大家学会 ...

  3. HI3531网络tftp、nfs加载

     ifconfig eth0 hw ether 00:00:23:34:45:66; ifconfig eth0 192.168.1.10 netmask 255.255.255.0; route a ...

  4. FusionCharts封装-dataset和categories

    Chart.java: /** * @Title:Chart.java * @Package:com.fusionchart.model * @Description:FusionCharts 封装d ...

  5. R语言︱非结构化数据处理神器——rlist包

    本文作者:任坤,厦门大学王亚南经济研究院金融硕士生,研究兴趣为计算统计和金融量化交易,pipeR,learnR,rlist等项目的作者. 近年来,非关系型数据逐渐获得了更广泛的关注和使用.下面分别列举 ...

  6. HighCharts之2D堆面积图

    HighCharts之2D堆面积图 1.HighCharts之2D堆面积图源码 StackedArea.html: <!DOCTYPE html> <html> <hea ...

  7. 基于 angular 规范的 commit

    基于 angular 规范的 commit commit格式如下: <type>: <subject> <BLANK LINE> <body> type ...

  8. Struts2的数据封装

    在很多的实际开发场景中,页面提交请求参数Action ,在Action中接收参数并对接收的数据进行封装.封装到一个JavaBean中,将JavaBean传递给业务层中.Struts2数据封装分为两类: ...

  9. 滚动条实现RGB颜色的调制(窗体程序)--JAVA基础

    1.用到的JFrame类的对象frame的方法: frame.setLayout(); 设置框架布局格式,有frame.setLayout(new GridLayout(5,1));为网格布局格式 f ...

  10. iOS超全开源框架、项目和学习资料汇总 UI篇

    上下拉刷新控件 MJRefresh --仅需一行代码就可以为UITableView或者CollectionView加上下拉刷新或者上拉刷新功能.可以自定义上下拉刷新的文字说明. AutoLayout ...