/*按地址传递*/ #include <iostream> using namespace std; void swap(int *a, int *b) { int c; c = *a; *a = *b; *b = c; } int main() { int a = 3, b = 4; cout << "交换前" << "a: " << a << " b: " << b << endl; swap(&a, &b); // 按地址传递 cout << "交换后" << "a: " << a << " b: " << b << endl; return 0; }
按别名传递
/*按别名传递*/ /*我们可以将a和b的别名传递到swap函数中,将swap函数的接收参数改为接收两个别名,调用时候将a和b传递进去即可, 这样参数a和b就变成了主函数中的a和b的别名,由于别名即自身,所以对别名的操作即是对main函数的a和b的操作, 或者说参数a和b即main函数中的a和b*/ #include <iostream> using namespace std; void swap(int &a, int &b) { int c; c = a; a = b; b = c; } int main() { int a = 3, b = 4; cout << "交换前" << "a: " << a << " b: " << b << endl; swap(a, b); cout << "交换后" << "a: " << a << " b: " << b << endl; return 0; }
按值传递比较消耗系统资源,下面来演示
/*按值传递对象*/ #include <iostream> using namespace std; class A { public: A() {cout << "执行构造函数创造一个对象 ";} A(A&) {cout << "执行复制函数创建该对象的副本 ";} ~A() {cout << "执行析构函数删除该对象 ";} }; A fun(A one) { return one; } int main() { A a; fun(a); return 0; }
上面代码如果改成按地址传递就会少调用一次复制构造函数,当然跟着也会少调用一次析构函数,当然如果仍然返回一个值的话,
仍然会调用一次复制构造函数来创建一个返回值,仍然浪费了系统资源,
#include <iostream> using namespace std; class A { public: A() {cout << "执行构造函数创造一个对象 ";} A(A&) {cout << "执行复制函数创建该对象的副本 ";} ~A() {cout << "执行析构函数删除该对象 ";} }; A fun(A *one) { return *one; } int main() { A a; fun(&a); return 0; }
当然如果仅仅返回一个地址的话,就会省去上面的因要返回一个值而调用复制构造函数和析构函数的过程
/*按值传递对象*/ #include <iostream> using namespace std; class A { public: A() {cout << "执行构造函数创造一个对象 ";} A(A&) {cout << "执行复制函数创建该对象的副本 ";} ~A() {cout << "执行析构函数删除该对象 ";} }; A *fun(A *one) { return one; } int main() { A a; fun(&a); return 0; }
按值传递虽然可以避免重复调用复制构造函数和析构函数 ,但是由于它得到了该对象的内存地址,可以随时修改该对象的数据。所以它实际上是破坏了按值传递的保护机制,不过我们仍然有解决办法,那就是用const指针来接收对象,这样就可以防止任何试图对该对象进行操作的行为,并且保证返回一个不可被修改的对象
这样的话我们无法通过返回的地址来修改对象的值,但是我们仍然可以通过该对象的名字来修改该对象。因为指向常量的长指针只是限制我们用该指针修改它指向的对象的值,但是它并不会改变原始对象的属性,不修改该对象的值的操作可以利用传递回来的地址进行,
注意:子函数返回的指向常量的长指针的值 必须用指向常量的长指针来接收;否则会报错!!!!!!!!!!!!!!!!!!!!!!!!
/*注意不要用对象来接收返回的别名,由于别名并不是对象,只是原来对象的另一个名字,所以呢执行复制构造函数以后,并没有*/ #include <iostream> using namespace std; class A { public: A() {cout << "执行构造函数创造一个对象 ";} A(A&) {cout << "执行复制函数创建该对象的副本 ";} ~A() {cout << "执行析构函数删除该对象 ";} int get()const{return x;} void set(int i) {x = i;} private: int x; }; const A *const fun(const A *const one) //保证传进来的数据不被修改,返回的值也不被修改 { one->get(); return one; } int main() { A a; const A *const p = fun(&a); a.set(11); cout << "p:" << p << endl; cout << "a:" << &a << endl; return 0; }
下面是引用的代码,注意当子函数返回一个引用的时候不能用一个对象来接收该引用,这个对象是不会得到一个对象的数据的,因为返回的仅仅是一个引用的名字而已,而要定义一个引用来接收返回的引用,如果返回一个对象的时候倒是可以定义一个对象来接收
/*注意不要用对象来接收返回的别名,由于别名并不是对象,只是原来对象的另一个名字, 即返回的不是对象,而是对象的另一个名字,所以不能用对象来接收一个别名,而要用一个别名来接收它 如果返回的是一个对象的话就可以用一个对象来接收它,这样执行复制构造函数的时候也会复制返回的对象的数据 所以执行复制构造函数之后并没有复制该对象的数据*/ /*别名常量不能修改它所引用的对象的数据const A &b = a; 即定义一个类A中a对象的别名常量b,*/ #include <iostream> using namespace std; class A { public: A() {cout << "执行构造函数创造一个对象 ";} A(A&) {cout << "执行复制函数创建该对象的副本 ";} ~A() {cout << "执行析构函数删除该对象 ";} int get()const{return x;} void set(int i) {x = i;} private: int x; }; A& fun(A &one) { one.get(); return one; } int main() { A a; A &b = fun(a); a.set(11); cout <<b.get()<< endl; return 0; }
引用本来就是常量,我们不能改变它所引用的对象,而且当我们不想让该引用修改它所引用的对象的值的时候,我们可以把它定义为别名常量,
这个时候用该引用修改原对象的名字就是非法的,会报错,只能用原对象的名字来修改这个对象
/*别名常量不能修改它所引用的对象的数据const A &b = a; 即定义一个类A中a对象的别名常量b,这时候不能用b修改a的数据*/ #include <iostream> using namespace std; class A { public: A() {cout << "执行构造函数创造一个对象 ";} A(A&) {cout << "执行复制函数创建该对象的副本 ";} ~A() {cout << "执行析构函数删除该对象 ";} int get()const{return x;} void set(int i) {x = i;} private: int x; }; const A& fun(const A &one) //保证返回的值不被修改,传进来的对象也不被修改 { one.get(); return one; } int main() { A a; const A &b = fun(a); a.set(11); cout <<b.get()<< endl; return 0; }