zoukankan      html  css  js  c++  java
  • C++引用具体解释

    引用是C++中新出现的。有别于C语言的语法元素之中的一个。

    关于引用的说明,网络上也有不少。可是总感觉云遮雾绕,让人印象不深刻。

    今天我就来深入解释一下引用。并就一些常见的观点进行说明,最后附带代码演示样例予以说明(注意。开发环境是vs2013)。


    前面先摆出我的观点:

    1 引用的出现纯粹是为了优化指针的使用,而提出的语法层面的处理。

    2 引用实现原理上全然等价于指针。

    3 引用对于传递对象參数有很大的优化和优点。

    4 引用有其局限性,与指针相比,有时候可能与面向对象的设计有冲突。


    以下给出我的样例。通过这个样例,我再来慢慢解释上面的观点:

    	void intreference(int& i)
    {
    	printf("[%s]i=%d
    ", __FUNCTION__, i);
    	i++;
    }
    void objectreference(std::string& str){
    	printf("[%s]str=%s
    ", __FUNCTION__, str.c_str());
    	str += 'i';
    }
    
    class mystr :public std::string
    {
    public:
    	mystr() :std::string(){}
    	~mystr(){
    	}
    };
    
    void testvirtual(mystr&str){
    	printf("[%s]str=%s
    ", __FUNCTION__, str.c_str());
    }
    
    class mytest{
    public:
    	mytest(){}
    	~mytest(){}
    	virtual void test(){
    		printf("father
    ");
    	}
    };
    
    class mysubtest:public mytest{
    public:
    	mysubtest(){}
    	~mysubtest(){}
    	virtual void test(){
    		printf("hello!
    ");
    	}
    };
    
    void testpurevirtual(mytest& test)
    {
    	test.test();
    }
    
    void main()
    {
    	char* p;
    	int i = 0;
    	refint refintfunc = (refint)intreference;
    	printf("i=%d
    ", i);
    	intreference(i);
    	printf("i=%d
    ", i);
    	refintfunc(&i);
    	printf("i=%d
    ", i);
    	refobj refobjfunc = (refobj)objectreference;
    	std::string obj = "s";
    	printf("str=%s
    ", obj.c_str());
    	objectreference(obj);
    	printf("str=%s
    ", obj.c_str());
    	refobjfunc(&obj);
    	printf("str=%s
    ", obj.c_str());
    	//int& j = i;
    	printf("i %08x,j %08x
    ", &i, &i);
    	std::string* pstr = new mystr();
    	//testvirtual(*pstr);//error C2664: “void testvirtual(mystr &)”: 无法将參数 1 从“std::string”转换为“mystr &”
    	mysubtest t;
    	testpurevirtual(t);
    	getchar();
    }
    	

    样例里面我给出了两个引用測试函数和一个变量引用

    样例说明了什么:

    1 引用实现原理上全然等价于指针

    请注意。函数intreference与函数objectreference是一个引用參数的函数

    而函数指针refintfunc与函数指针refobjfunc是一个指针參数的函数指针

    对于后者的调用。编译器会毫不迟疑的将i的地址传递给函数

    假设引用參数实现原理与指针不全然等价,那么必定会导致函数调用出现故障

    但结果却非常有趣,我发现两种方式,效果全然同样,没有不论什么差异。

    以下是执行时的反汇编:

    从反汇编能够清晰的看到,对于直接进行引用參数函数调用。和使用指针參数调用。两者的汇编代码全然没有什么差别

    引用在实现的时候,传递的就是一个指针给函数!!

    不不过对于简单数据类型如此,对于复杂数据类型这也是相同的:

    能够看到,两者都是将obj的地址作为參数,放入到了eax,然后再推送到栈中去了

    也就是说,在实现层面上面,两者是等同的

    那么对于局部,非參数传递的引用呢?

    以下是局部引用j和其引用对象i赋值时的反汇编:

    注意第一条红线,j是有自己的栈空间地址的!并不是如同网络上所说的别名。不占用空间,等价等等。不是这种!

    它仍然要占空间,占一个指针大小的空间。假设i和j是char和char引用,那么j占用的空间甚至比i还大!

    在赋值的时候。系统将i的地址给eax。然后再通过eax寄存器将地址传入j,注意dword ptr,这表示指针j!

    这和我前面提到的观点:引用实现原理上全然等价于指针 是全然一致的。

    >

    <

    2 既然它在实现层面上全然等价于指针。那为什么还会有引用?

    这就要回到我前面提出的第一个观点:引用的出现纯粹是为了优化指针的使用,而提出的语法层面的处理

    假设这里使用指针。就会很麻烦!

    首先,假设函数的參数是指针。开发者就必需要要验证指针!这个差点儿是无法避免的情况!

    否则指针一旦为空,整个程序必定崩溃。

    可是引用就避免了这个麻烦——通过语法层面上的干预——使得用户无法显式的传递空指针到函数中去

    假设有空指针或者野指针,崩溃仅仅会发生在函数外部,而非内部。

    其次。输入.比输入->更加让开发者开心一些,不论是长度还是安全性上,指针式的成员函数调用,总让人心惊胆颤

    因此,引用全然是一种语法层面的处理。就是C++中的私有成员变量一样,仅仅是从语法上阻止用户去显式訪问——实际上能够利用指针,强制从内存中读写该变量。

    当然引用不只不过这样,之所以面向对象要增加引用,另外一个作用还在于:

    假设參数纯粹是一个对象,那么意味着程序须要频繁的在栈上面构造和析构对象。

    而引用成功的攻克了这个问题。能够让开发者决定要不要在栈上面构造对象并自己主动析构它。

    这样导致效率极大的提升了——非常多复杂的对象。其构造函数和复制构造函数可能异常复杂和耗时。

    同一时候,另外一些对象可能并不希望调用者使用它们的构造函数。比方单例对象!

    而引用非常好的攻克了这个矛盾。

    3引用有没有限制?答案是有!

    限制在哪里?我们知道。面向对象设计中有接口这个概念,而C++与之关联的是虚函数。

    我们常常会持有一个父类的指针,而在当中填入各种子类的对象,然后通过虚函数去调用相应的子类接口实现。

    可是这里使用引用却有限制。仅仅能在声明为父类引用的时候。使用子类,而无法在声明为子类引用的时候使用父类。

    指针却能够不受此限制,进行自由的转化(当然这是有风险的!

    以下给出了一个演示样例:

    对于mystr和函数testvirtual,假设传入一个父类对象(实际上还是一个子类,仅仅是是一个父类指针),在语法上这是被禁止!

    对于mytest和mysubtest以及函数testpurevirtual,这样又是能够的。

    这种限制要求开发人员在设计的时候就必须很仔细,事先想好接口的统一性。否则后面代码就有的改了

    当然,这样也有优点,能够避免一些问题。比方空指针或者对象不匹配异常(将一个非mytest或者其子类的对象指针强制转化过来。此时调用必定崩溃。)

    <pre code_snippet_id="1639674" snippet_file_name="blog_20160408_34_61346" name="code" class="cpp">class mystr :public std::string
    {
    public:
    	mystr() :std::string(){}
    	~mystr(){
    	}
    };
    
    void testvirtual(mystr&str){
    	printf("[%s]str=%s
    ", __FUNCTION__, str.c_str());
    }
    
    class mytest{
    public:
    	mytest(){}
    	~mytest(){}
    	virtual void test(){
    		printf("father
    ");
    	}
    };
    
    class mysubtest:public mytest{
    public:
    	mysubtest(){}
    	~mysubtest(){}
    	virtual void test(){
    		printf("hello!
    ");
    	}
    };
    
    void testpurevirtual(mytest& test)
    {
    	test.test();
    }
    
    void main()
    {
    	int i = 0;
    	refint refintfunc = (refint)intreference;
    	printf("i=%d
    ", i);
    	intreference(i);
    	printf("i=%d
    ", i);
    	refintfunc(&i);
    	printf("i=%d
    ", i);
    	refobj refobjfunc = (refobj)objectreference;
    	std::string obj = "s";
    	printf("str=%s
    ", obj.c_str());
    	objectreference(obj);
    	printf("str=%s
    ", obj.c_str());
    	refobjfunc(&obj);
    	printf("str=%s
    ", obj.c_str());
    	int& j = i;
    	printf("i %08x,j %08x
    ", &i, &j);
    	std::string* pstr = new mystr();
    	//testvirtual(*pstr);//error C2664: “void testvirtual(mystr &)”: 无法将參数 1 从“std::string”转换为“mystr &”
    	mysubtest t;
    	testpurevirtual(t);
    	getchar();
    }
    最后给出执行结果的截图:

    能够看到。结果充分说明了引用事实上就是指针

    这里补充说明一下i和j的问题:

    当我声明了j的时候,能够看到函数栈的大小


    而没有声明j的时候。函数栈明显变小了


    小了12字节,非常奇怪,好像和指针的大小不一致啊

    没有关系,我再声明一个指针,我们再看看


    看到没有?栈又恢复到了14c了。而我仅仅是声明了一个char*p,而且没有做不论什么调用。

    这说明j是占领空间的,大小正好是一个指针!!

    
    

  • 相关阅读:
    noip2016组合数问题
    noip2017奶酪
    洛谷1091合唱队形
    洛谷P1075 质因数分解
    洛谷1004方格取数
    POJ2393奶酪工厂
    NOIP2012国王游戏(60分题解)
    洛谷1106删数问题
    洛谷1209修理牛棚
    二维树状数组区间修改+区间查询模版
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/7290060.html
Copyright © 2011-2022 走看看