c++

c++对于指针引用还有方程内内存的思考

Posted by Roy Zhang on 2019-08-03

1.C++方程与指针,引用


对于c++方程来说,有两个地方是关键点,一个是方程的参数传入,一个是方程返回值。直接总结:

  • 传入参数和返回值都是默认的浅拷贝,传入值是会产生一个副本,返回值也是会产生副本的。对于指针传入值,不能在方程中分配内存,因为实际指针并没有变化,而引用值可以引用只是内存的别名只是换了个名字而已。
  • 返回值是指针必须看下指向的地方是不是heap上,因为栈内的地址会随着方程结束而自动栈消亡。同理也是引用。当返回值是类的时候,要确认是不是拷贝的时候是深复制。还有一个引用作为参数传入这个比较管用const std::string &
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Example program
#include <iostream>
#include <string>
int test(int a, int b)
{
std::cout<<"a dizhi: " <<&a <<" b dizhi :"<<&b<<std::endl;
int c = a+b;
std::cout<<" c dizhi: " <<&c<<std::endl;
return c;

}



int main()
{
int i = 3;
int j = 4;

std::cout<<" i dizhi: " <<&i <<" j dizhi :"<<&j<<std::endl;
int g = test(i ,j);
std::cout<<" g dizhi: " <<&g<<std::endl;

}

结果

1
2
3
4
i dizhi: 0x722a4acd7314 j dizhi :0x722a4acd7318
function :a dizhi: 0x722a4acd72dc b dizhi :0x722a4acd72d8
function :c dizhi: 0x722a4acd72ec
g dizhi: 0x722a4acd731c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Example program
#include <iostream>
#include <string>

int testA (void)
{
int b = 1 ;
return b ;
}

char * testB (void)
{
char str[] = "abc" ;
return str ; //但这样的方法是不推荐的
}

int main()

{
printf( " the value of testA is %d \n", testA() ) ;
printf( " the value of testB is %c ", *( testB() ) ) ;

}

对于返回值的情况:

testA与main函数同在栈区,testA结束时C++创建临时变量,然后将返回值复制给该临时变量。printf( " the value of testA is %d \n", testA() ) 时输出的是该临时变量的值,testA中的b已经不存在。

对于返回指针的情况:这是最复杂的部分。首先,对于上面的情形:返回一个数组的首地址,由于是返回char *类型,所以C++会首先创建一个char 类型的临时变量,再把该数组的首地址赋给临时变量;函数结束后该数组也就被销毁,这就意味着临时变量指向了一个“未声明的地址”,幸运的情况下,这段内存暂时还没有被其他的数据所覆盖,因此还能输出正确的内容。在testB里面,如果换成char str=“abc”;return str; 由于这时str指向的是全局数据区的一段内存地址,所以函数结束后临时变量也指向该地址,所以编译器不会提出警告。但这样的方法是不推荐的。

返回引用:这中情况的效率最高,它直接返回一个对象,不产生返回值的副本。但同时也要注意避免返回局部引用的情况。

如果在某个函数内部有一个A类的局部变量,比如:

A a;

return a;

这时候也会返回a的一个拷贝,如果A没有写深拷贝构造函数,就会调用缺省的拷贝构造函数(浅拷贝),这样做就会失败的;

如果A中提供了深拷贝构造函数,则这样做就是可以的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <iostream>
using namespace std;
int some_fun1()
{
cout << "\nsome_fun1()..." << endl;
int a = 5;
return a; //OK
}



int* some_fun2()
{
cout << "\nsome_fun2()..." << endl;
int a = 5;
int *b = &a;
return b; // not OK
}



int* some_fun3()
{

cout << "\nsome_fun3()..." << endl;
int *c = new int(5);
return c; // OK, return c执行完后,并没被销毁(必须要用delete才能销毁)

}



class CSomething
{
public:
int a;
int b;
public:

CSomething(int a, int b)
{
this->a = a;
this->b = b;
}
};



class CA
{
public:
CA(CSomething* sth)
{
this->sth = new CSomething(sth->a, sth->b);
}

// 如果不实现深拷贝,请注释这个拷贝构造函数
CA(CA& obj)
{
sth = new CSomething((obj.sth)->a, (obj.sth)->b);
}
~CA()
{
cout << "In the destructor of class CA..." << endl;
if (NULL != sth)
{
delete sth;
}
}

void Show()
{
cout << "(" << sth->a << ", " << sth->b << ")" << endl;
}

void setValue(int a, int b)
{
sth->a = a;
sth->b = b;
}

void getSthAddress()
{
cout << sth << endl;
}

private:
CSomething* sth; // 以指针形式存在的成员变量

};

CA some_fun4()
{
cout << "\nsome_fun4()..." << endl;
CSomething c(1, 2);
CA a(&c);
cout << "\\ some_fun4()..." << endl;
return a; // 如果CA没有实现深拷贝,则not OK;如果实现深拷贝,则OK

}


int main(int argc, char* argv[])
{

int a = some_fun1();
cout << a << endl; // OK

int *b = some_fun2();
cout << *b << endl; // not OK,即便返回结果正确,也不过是运气好而已

int *c = some_fun3(); // OK, return c执行完后,c并没有被销毁(必须要用delete才能销毁)
cout << *c << endl;
delete c;

CA d = some_fun4(); // 如果CA没有实现深拷贝,则not OK;如果实现深拷贝,则OK
d.Show();

return 0;

}