一个关于函数参数引用的疑问

sxqinge 2014-09-02 06:04:57
刚开始我问了个问题,http://bbs.csdn.net/topics/390876262?page=1#post-398111538,在这里感谢大家让我了解了拷贝函数的使用。
但是又出现了一个新问题,这里另开一贴。
为了方便阅读,先把代码贴上:

#include <iostream>

using namespace std;
class test{
public:
int *p;
test(int value){
p = new int(value);
}
~test(){
delete p;
p = NULL;
}

test(const test &other){
p = new int(*other.p);
}

test & operator = (const test &pt){
if( this == &pt ) return *this;
delete p;
p = new int(*pt.p);
return *this;
}

bool operator == (const test &other){
return p == other.p;
}

friend ostream & operator << ( ostream &os, test &A ){
os << *A.p;
return os;
}
void printvalue(){
cout<<"the value is "<<*p<<endl;
}
};

void func(test t){
cout<<"in the func value is "<< t <<endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
test t1 = 33;
func(t1);
t1.printvalue();
system("pause");
return 0;
}


我的问题是func函数参数的使用。
1、如果该参数不加引用(void func(test t)),那么调用该函数时相当于产生了一个test对象,当退出该函数后会进行test的析构操作。问题是,我传入t1,那么t和t1应该是指向同一块内存的,对t进行析构时,t1指向的内存应该也同时被删除了。但是调试时发现并未如此,执行完func(t1)后,再次执行打印t1.printvalue()操作,发现值仍为33,说明t1指向的内存并未被删除。
2、如果该参数加了引用(void func(test &t))。那么此时的参数t传入的是t1的地址,在执行完func函数后,不会执行析构操作。
对于以上两点现象,因为我对指针不怎么了解,所以对这里感觉很疑惑,希望大侠帮忙解答下,谢谢!
...全文
250 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
lm_whales 2014-09-03
  • 打赏
  • 举报
回复
因为正确实现了拷贝构造函数和赋值运算符函数,所以函数调用 func(t1);实现了真正的值传递语义 所以这个程序很正常,即便函数内部修改了形参t,也不会改变 实参 t1的值。
默伊清风 2014-09-03
  • 打赏
  • 举报
回复
引用 5 楼 lisong694767315 的回复:
当你按传递的时候,即void func(test t),形参是实参的拷贝,即会调用拷贝构造函数将 t1 拷贝给 t ,改变 t 的值并不会影响外部 t1 的值。 而当你传递引用的时候,即void func(test& t),t 相当于是实参 t1 的“别名”,对 t 的操作其实就是对 t1 的操作。 在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
解释的很好,学习了
赵4老师 2014-09-03
  • 打赏
  • 举报
回复
理解讨论之前请先学会如何观察! 计算机组成原理→DOS命令→汇编语言→C语言(不包括C++)、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言(包括C++)、架构…… 对学习编程者的忠告: 多用小脑和手,少用大脑、眼睛和嘴,会更快地学会编程! 眼过千遍不如手过一遍! 书看千行不如手敲一行! 手敲千行不如单步一行! 单步源代码千行不如单步Debug版对应汇编一行! 单步Debug版对应汇编千行不如单步Release版对应汇编一行! 单步类的实例“构造”或“复制”或“作为函数参数”或“作为函数返回值返回”或“参加各种运算”或“退出作用域”的语句对应的汇编代码几步后,就会来到该类的“构造函数”或“复制构造函数”或“运算符重载”或“析构函数”对应的C/C++源代码处。 VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化,这样过一遍不就啥都明白了吗。 对VC来说,所谓‘调试时’就是编译连接通过以后,按F10或F11键单步执行一步以后的时候,或者在某行按F9设了断点后按F5执行停在该断点处的时候。 (Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)
白衣如花 2014-09-03
  • 打赏
  • 举报
回复
传值,函数会调用“拷贝构造函数”新生成一个对象 在函数结束时对这个对象进行析构 由于“拷贝构造函数”函数中用了new,所以这个临时对象所指向的value并不是传进来的那个参数 当然析构掉也不会对原来的对象造成影响 我比较好奇
bool operator == (const test &other){         return p == other.p;     } 
这个会出现==的情况吗?
勤奋的小游侠 2014-09-03
  • 打赏
  • 举报
回复
其实就是拷贝构造函数和赋值运算符函数的问题。 这个搞通了,其它的自然就通了。
白衣如花 2014-09-03
  • 打赏
  • 举报
回复
引用 17 楼 sxqinge 的回复:
[quote=引用 15 楼 u012948520 的回复:] 传值,函数会调用“拷贝构造函数”新生成一个对象 在函数结束时对这个对象进行析构 由于“拷贝构造函数”函数中用了new,所以这个临时对象所指向的value并不是传进来的那个参数 当然析构掉也不会对原来的对象造成影响 我比较好奇
bool operator == (const test &other){         return p == other.p;     } 
这个会出现==的情况吗?
在复制函数中我做了个判断 if( *this == pt ) return *this; 这段话的意思就是判断是否自己给自己赋值,这里的''=="就得自己重载了。 感谢大家的详细描述,我基本了解得差不多了。[/quote] 你判断两个指针相等还是判断两个指针指向的数据相等? 估计是return *p == *(other.p);吧?
sxqinge 2014-09-03
  • 打赏
  • 举报
回复
引用 15 楼 u012948520 的回复:
传值,函数会调用“拷贝构造函数”新生成一个对象 在函数结束时对这个对象进行析构 由于“拷贝构造函数”函数中用了new,所以这个临时对象所指向的value并不是传进来的那个参数 当然析构掉也不会对原来的对象造成影响 我比较好奇
bool operator == (const test &other){         return p == other.p;     } 
这个会出现==的情况吗?
在复制函数中我做了个判断 if( *this == pt ) return *this; 这段话的意思就是判断是否自己给自己赋值,这里的''=="就得自己重载了。 感谢大家的详细描述,我基本了解得差不多了。
passion_wu128 2014-09-02
  • 打赏
  • 举报
回复
大家都讲得很好,太晚了,我就不废话了,睡觉去了。
15111197410 2014-09-02
  • 打赏
  • 举报
回复
形参传值时调用拷贝构造函数,当函数调用结束时调用析构函数,此时析构的是临时的形参对象t,而不会影响实参t1。 形参传地址(即引用)时,不调用拷贝构造函数,形参和实参指向同一块地址,函数调用完成后也不会调用析构函数,所以实参t1仍然不变,直到主函数结束时才会调用析构函数。
mujiok2003 2014-09-02
  • 打赏
  • 举报
回复
首先, 在函数调用的时候, 实参会被用来初始化形参。

void foo(int x){
}
int j;
foo(j); //实参j会被用来初始化x, 相当于int x = j;
所以你只需要明白“在对象/变量的定义(的同时初始化)”就够了。

std::string a = "hello"; //调用构造函数std::string(char const*)
std::string b = a; //copy, 调用了拷贝构造函数
std::string& c = a; //no copy,没有调用构造函数
//c++11 中还有move构造函数
FeelTouch Labs 2014-09-02
  • 打赏
  • 举报
回复
楼主我重新构成了你的代码,这下配合输出你应该能明白了
代码如下:
using namespace std;

class test{
int *p;
public:
test(int value){
p = new int(value);
cout<<"Execute Constructor"<<endl;
cout<<"p的地址为"<<&p<<"前p的值为"<<p <<" *p的值为"<<*p<<endl;
}
~test(){
cout<<"Execute Destructor"<<endl;
delete p;
p = NULL;
}

test(const test &other){
cout<<"Execute Copy Constructor"<<endl;
p = new int(*other.p);
}

test & operator = (const test &pt){
cout<<"Test operator"<<endl;
cout<<"前p的地址为"<<&p<<"前p的值为"<<p <<" *p的值为"<<*p<<endl;
p = new int(*pt.p);
cout<<"前p的地址为"<<&p<<"后p的值为"<<p <<" *p的值为"<<*p<<endl;
return *this;
}

bool operator == (const test &other){
return p == other.p;
}

friend ostream & operator << ( ostream &os, test &A ){
os << *A.p;
return os;
}
void printvalue(){
cout<<"the value is "<<*p<<endl;
}
};

void func(test t){
cout<<"Waiting... "<<endl;
t.operator=(5);
cout<<"in the func value is "<< t <<endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
test t1 = 33;
func(t1);
t1.printvalue();
return 0;
}

输出如下:
M_HANDLE 2014-09-02
  • 打赏
  • 举报
回复
呵呵,可能是你对我上篇帖子说的有点误解。t = t1 ,t和t1是不同的对象,怎么可能会指向同一块内存。没重新定义复制构造函数前,默认复制构造函数执行时会执行:t.p = t1.p,所以t.p和t1.p指向同一块内存。如果你能看懂下面的代码存在的问题,就应该理解了。
 int *p = new int(5);
    int *p1 = p;
    delete p;
    cout<<*p1<<endl;//不会输出5
但你重新定义了复制构造函数后,相当于下面的代码:
 int *p = new int(5);
    int *p1 = new int(*p);
    delete p;
    cout<<*p1<<endl;//输出5
FeelTouch Labs 2014-09-02
  • 打赏
  • 举报
回复
引用 1 楼 sxqinge 的回复:
是我看错了。。。不加引用时参数 t 指针值与 t1 并不相同。这里我弱弱的问句,指针值相同的话,是不是指向的地址(地址是不是内存块?)是一样的?
指针值相同,代表指向同一块内存地址,也就是内存块,在同一时间是一样的
神奕 2014-09-02
  • 打赏
  • 举报
回复
当你按传递的时候,即void func(test t),形参是实参的拷贝,即会调用拷贝构造函数将 t1 拷贝给 t ,改变 t 的值并不会影响外部 t1 的值。 而当你传递引用的时候,即void func(test& t),t 相当于是实参 t1 的“别名”,对 t 的操作其实就是对 t1 的操作。 在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
passion_wu128 2014-09-02
  • 打赏
  • 举报
回复
要下班了,晚上有事。看你这么认真,晚上回来详细给你讲下引用和你说的问题。
勤奋的小游侠 2014-09-02
  • 打赏
  • 举报
回复
我只回答你的问题1,delete一块内存,只是告诉系统,这块内存还给你了,但是内存的内容不一定会被清空。只要系统不用,里面的值就不会变。你依然可以用指向这块内存的指针访问,但有可能会出错。
jwj070524 2014-09-02
  • 打赏
  • 举报
回复
1)你写了正确的拷贝构造函数,所以没有发生问题 2)编译器底层就是用指针实现引用的
sxqinge 2014-09-02
  • 打赏
  • 举报
回复
是我看错了。。。不加引用时参数 t 指针值与 t1 并不相同。这里我弱弱的问句,指针值相同的话,是不是指向的地址(地址是不是内存块?)是一样的?

64,690

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧