C++引用指针 & 构造函数
近来回转 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. 若未显示定义,系统会生成默认缺省的拷贝构造函数。缺省的拷贝构造函数会依次拷贝类成员进行初始化
何时调用拷贝函数:
- 一个对象以值传递的方式传入函数体
- 一个对象以值传递的方式从函数返回(与返回值优化密切相关)
- 一个对象需要通过另一个对象进行初始化
关于拷贝构造函数还有一类很热的问题,构造函数拷贝赋值函数的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++引用指针 & 构造函数的更多相关文章
- C++ const 引用 指针
先简单回忆一下常量的性质: int main() { const int buffSize = 512; buffsize = 512; //× buffSize是常量 } 初始化时: const i ...
- perl5 第十二章 Perl5中的引用/指针
第十二章 Perl5中的引用/指针 by flamephoenix 一.引用简介二.使用引用三.使用反斜线(\)操作符四.引用和数组五.多维数组六.子程序的引用 子程序模板七.数组与子程序八.文件句 ...
- 引用&指针交换函数实践
实践如下: #include <iostream> using namespace std; // 普通交换,注意这里的ab值,在具体调用时是基本数据的拷贝,原始数据不会变化 // 因此这 ...
- C++ 引用、构造函数、移动语义
1.引用 C++中的引用主要用作函数的形参,接近于const指针,必须在创建时初始化. 以Person类为例,如下: Person p; //调用P的 ...
- c/c++ 函数指针 指针函数 数组的引用 指针数组 数组指针
1.指针数组数组指针 引用数组 数组的引用 int *a[10] 指针数组 每一个元素都是一个指针 Int (*a)[10] 数组指针 P指向一个含有10个元素的数组 Int (&a)[10] ...
- C++ Primer 第二章 引用 指针 const限定符
1.引用: 为对象起了另外一个名字,引用类型引用另外一种类型,通过将声明符写成&d的形式来定义引用类型,其中d也就是声明的变量名(声明符就是变量名). PS:1.通过图片中编译所提示的报错信息 ...
- 子类父类(虚函数下的 引用指针 对象)->看来没有子类指针这回事
#include<iostream> using namespace std; class Father { public: Father() { cout << " ...
- python3.x 类似cpp引用指针传参修改
#同名局部变量调用外部全局变量: num=100def fun(): global num#告诉编译器是全局的num num+=100 print(num)print(fun)print(fun()) ...
- C++ 引用 指针 使用举例
1. 请看下程序 inline void CScanLineFill::removeOldNodeAET(AET* &aetList, const float yCurrent) { AET* ...
随机推荐
- 第1章 PCI总线的基本知识
PCI总线作为处理器系统的局部总线,主要目的是为了连接外部设备,而不是作为处理器的系统总线连接Cache和主存储器.但是PCI总线.系统总线和处理器体系结构之间依然存在着紧密的联系. PCI总线作为系 ...
- 关于SSDT
百度上比较好的解释是:SSDT的全称是System Services Descriptor Table,系统服务描述符表.这个表就是一个把ring3的Win32 API和ring0的内核API联系起来 ...
- windows 7蓝屏解决办法
1.通过工具打开出现蓝屏原因的dmp文件,找到原因 Unable to load image \SystemRoot\system32\ntkrnlpa.exe, Win32 error 0n2 2. ...
- 【mongodb系统学习之三】进入mongodb shell
三. 进入mongodb shell(数据库操作界面) : 1).在mongodb的bin目录下输入./mongo,默认连接test数据库,连接成功会显示数据库版本和当前连接的数据库名,如图: 2). ...
- windows驱动之WDF---CharSample
驱动程序部分: NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ ...
- java暴力递归回溯算法
今天这个问题是我之前一直想解决的,还记得以前第一次上蓝桥杯的课的时候,也就是大一高数期中模拟考试那天,下午去上蓝桥杯课,遇到这道题,当时写了写,根本没有思路,然后就给大一的模拟考试去了.印象深刻啊,一 ...
- day9(Hash、字典)
一.Hash 简介: Hash,一般被翻译成"散列",也有直接音译"哈希"的,就是把任意长度的输入,通过哈希算法,变换成固定长度的输出,输出的结果就叫做哈希值, ...
- Dynamics 365中审核用户权限变化的一种方法
摘要: 本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复268或者20180311可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyon ...
- 【BZOJ1096】【ZJOI2007】仓库建设(斜率优化,动态规划)
[BZOJ1096][ZJOI2007]仓库建设(斜率优化,动态规划) 题面 Description L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚.由于这座山处于高原 ...
- 【CJOJ P1333】【HNOI2012】矿场搭建
[HNOI2012]矿场搭建 Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖 ...