Top

CSDSTDCPP01 DAY03

  1. 返回有效引用
  2. 定义用户类并实例化为对象

1 返回有效引用

1.1 问题

函数返回值通常都具有右值属性,在函数的调用者空间根据函数的返回类型创建一个匿名对象,负责接收该函数的返回值,该匿名对象一般是临时对象。如果函数返回的是一个引用,那么用于接收该返回值的就不再是一个临时对象,而是一个引用该函数所返回引用的目标对象的引用,甚至可以是左值引用。

1.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:返回全局、静态、成员变量的引用

代码如下所示:

  1. #include <iostream>
  2.  
  3. int g = 123;
  4. struct Dummy {
  5. int m;
  6. int& foo (void) { returng; }
  7. int& bar (void) { staticint s = 456; return s; }
  8. int& hum (void) { returnm; }
  9. };
  10.  
  11. int main(int argc, constchar * argv[])
  12. {
  13.  
  14. Dummy dummy = {789};
  15. int&rg = dummy.foo ();
  16. std::cout<<"rg = "<< rg << std::endl;
  17. int&rs = dummy.bar ();
  18. std::cout<<"rs = "<< rs << std::endl;
  19. int&rm = dummy.hum ();
  20. std::cout<<"rm = "<< rm << std::endl;
  21.  
  22. return0;
  23. }

上述代码中,以下代码:

  1. int& foo (void) { returng; }

定义了结构体Dummy的一个成员函数,该函数返回全局变量g的一个引用。

上述代码中,以下代码:

  1. int&rg = dummy.foo ();

定义了一个引用rg,该引用通过返回引用的函数foo使其成为全局变量g的别名,所以,下述语句:

  1. std::cout<<"rg = "<< rg << std::endl;

输出的是全局变量g的值。

上述代码中,以下代码:

  1. int& bar (void) { staticint s = 456; return s; }

定义了结构体Dummy的一个成员函数,该函数返回静态局部整型变量s的一个引用。

上述代码中,以下代码:

  1. int&rs = dummy.bar ();

定义了一个引用rs,该引用通过返回引用的函数bar使其成为静态局部整型变量s的别名,所以,下述语句:

  1. std::cout<<"rs = "<< rs << std::endl;

输出的是静态局部整型变量s的值。

上述代码中,以下代码:

  1. int& hum (void) { returnm; }

定义了结构体Dummy的一个成员函数,该函数返回结构体成员变量m的一个引用。

上述代码中,以下代码:

  1. int&rm = dummy.hum ();

定义了一个引用rm,该引用通过返回引用的函数hum使其成为机构体成员变量m的别名,所以,下述语句:

  1. std::cout<<"rm = "<< rm << std::endl;

输出的是结构体成员变量m的值。

步骤二:返回调用对象自身的引用

代码如下所示:

  1. #include <iostream>
  2.  
  3. int g = 123;
  4. struct Dummy {
  5. int m;
  6. int& foo (void) { returng; }
  7. int& bar (void) { staticint s = 456; return s; }
  8. int& hum (void) { returnm; }
  9. Dummy& up (void) { ++m; return *this; }
  10. };
  11.  
  12. int main(int argc, constchar * argv[])
  13. {
  14.  
  15. Dummy dummy = {789};
  16. int& rg = dummy.foo ();
  17. std::cout<<"rg = "<< rg <<std::endl;
  18. int& rs = dummy.bar ();
  19. std::cout<<"rs = "<< rs <<std::endl;
  20. int& rm = dummy.hum ();
  21. std::cout<<"rm = "<< rm <<std::endl;
  22.  
  23. dummy.up().up().up();
  24. std::cout<<"rm = "<< rm <<std::endl;
  25.  
  26. return0;
  27. }

上述代码中,以下代码:

  1. Dummy& up (void) { ++m; return *this; }

定义了结构体Dummy的一个成员函数,该函数返回结构体自身*this的一个引用。

上述代码中,以下代码:

  1. dummy.up().up().up();

相当于写成如下方式:

  1. (dummy.up().up().up();

因为dummy.up()函数的返回值就是dummy自身,所以(dummy.up()).up()在第一个up函数返回后等效于(dummy).up(),依次类推。

步骤三:返回堆变量的引用

代码如下所示:

  1. #include <iostream>
  2.  
  3. int g = 123;
  4. struct Dummy {
  5. int m;
  6. int& foo (void) { returng; }
  7. int& bar (void) { staticint s = 456; return s; }
  8. int& hum (void) { returnm; }
  9. Dummy& up (void) { ++m; return *this; }
  10. };
  11.  
  12. int& square (int x) {
  13. int* y = newint;
  14. *y = x * x;
  15. return *y;
  16. }
  17.  
  18. int main(int argc, constchar * argv[])
  19. {
  20.  
  21. Dummy dummy = {789};
  22. int& rg = dummy.foo ();
  23. std::cout<<"rg = "<< rg <<std::endl;
  24. int& rs = dummy.bar ();
  25. std::cout<<"rs = "<< rs <<std::endl;
  26. int& rm = dummy.hum ();
  27. std::cout<<"rm = "<< rm <<std::endl;
  28.  
  29. dummy.up().up().up();
  30. std::cout<<"rm = "<< rm <<std::endl;
  31.  
  32. int& y = square(16);
  33. std::cout<< y <<std::endl; // 256
  34. delete&y;
  35.  
  36. return0;
  37. }

上述代码中,以下代码:

  1. int& square (int x) {
  2. int* y = newint;
  3. *y = x * x;
  4. return *y;
  5. }

定义了一个返回堆上变量引用的函数,在该函数体中,在堆上申请了一块儿存储空间,并返回该存储空间的引用。

上述代码中,以下代码:

  1. int&y = square(16);

定义了一个引用y,该引用是对上申请的一块儿空间的别名。引用y实质上是一种“野引用”或“悬空引用”。

步骤四:返回引用型参数本身

代码如下所示:

  1. #include <iostream>
  2.  
  3. int g = 123;
  4. struct Dummy {
  5. int m;
  6. int& foo (void) { returng; }
  7. int& bar (void) { staticint s = 456; return s; }
  8. int& hum (void) { returnm; }
  9. Dummy& up (void) { ++m; return *this; }
  10. };
  11.  
  12. int& square (int x) {
  13. int* y = newint;
  14. *y = x * x;
  15. return *y;
  16. }
  17.  
  18. intconst& max (intconst& a, intconst& b)
  19. {
  20. return a > b ? a : b;
  21. }
  22.  
  23. int main(int argc, constchar * argv[])
  24. {
  25.  
  26. Dummy dummy = {789};
  27. int& rg = dummy.foo ();
  28. std::cout<<"rg = "<< rg <<std::endl;
  29. int& rs = dummy.bar ();
  30. std::cout<<"rs = "<< rs <<std::endl;
  31. int& rm = dummy.hum ();
  32. std::cout<<"rm = "<< rm <<std::endl;
  33.  
  34. dummy.up().up().up();
  35. std::cout<<"rm = "<< rm <<std::endl;
  36.  
  37. int& y = square(16);
  38. std::cout<< y <<std::endl; // 256
  39. delete&y;
  40.  
  41. int a = 123, b = 456;
  42. std::cout<<&a <<' '<<&b <<std::endl;
  43. intconst& c = max (a, b);
  44. std::cout<<&c <<" : "<< c <<std::endl;
  45.  
  46. return0;
  47. }

上述代码中,以下代码:

  1. intconst& max (intconst& a, intconst& b)
  2. {
  3. return a > b ? a : b;
  4. }

定义了一个返回常引用的函数,该函数有两个常引用形参,返回的是大的那个常引用形参。

上述代码中,以下代码:

  1. intconst& c = max (a, b);

定义了一个常引用c,它将是整型变量a或b的别名,至于最终是哪一个变量的别名,取决于哪个整型变量的值大。在本例中,由于a被初始化成了123,b被初始化成了456,b大于a,所以常引用c就是变量b的别名。

步骤五:不能返回局部变量的引用

代码如下所示:

  1. #include <iostream>
  2.  
  3. int g = 123;
  4. struct Dummy {
  5. int m;
  6. int& foo (void) { returng; }
  7. int& bar (void) { staticint s = 456; return s; }
  8. int& hum (void) { returnm; }
  9. Dummy& up (void) { ++m; return *this; }
  10. };
  11.  
  12. int& square (int x) {
  13. int* y = newint;
  14. *y = x * x;
  15. return *y;
  16. }
  17.  
  18. intconst& max (intconst& a, intconst& b)
  19. {
  20. return a > b ? a : b;
  21. }
  22.  
  23. int& foo (void)
  24. {
  25. int n = 123;
  26. return n;
  27. }
  28.  
  29. int main(int argc, constchar * argv[])
  30. {
  31.  
  32. Dummy dummy = {789};
  33. int& rg = dummy.foo ();
  34. std::cout<<"rg = "<< rg <<std::endl;
  35. int& rs = dummy.bar ();
  36. std::cout<<"rs = "<< rs <<std::endl;
  37. int& rm = dummy.hum ();
  38. std::cout<<"rm = "<< rm <<std::endl;
  39.  
  40. dummy.up().up().up();
  41. std::cout<<"rm = "<< rm <<std::endl;
  42.  
  43. int& y = square(16);
  44. std::cout<< y <<std::endl; // 256
  45. delete&y;
  46.  
  47. int a = 123, b = 456;
  48. std::cout<<&a <<' '<<&b <<std::endl;
  49. intconst& c = max (a, b);
  50. std::cout<<&c <<" : "<< c <<std::endl;
  51.  
  52. int&z = foo();
  53. std::cout<<"z = "<< z <<std::endl;
  54.  
  55. return0;
  56. }

上述代码中,以下代码:

  1. int& foo (void)
  2. {
  3. int n = 123;
  4. return n;
  5. }

是错误的。因为函数foo返回了局部变量n的引用。由于当函数foo运行结束时,变量n所占的存储空间已经被释放,所以返回的引用就没有一块儿合法的存储空间相对应了,这样必然带来不测的后果。所以下面的代码中:

  1. int&z = foo();

引用z所对应的存储空间是一块儿已经被释放的存储空间,这样使用z将是危险的。

1.3 完整代码

本案例的完整代码如下所示:

  1. #include <iostream>
  2.  
  3. int g = 123;
  4. struct Dummy {
  5. int m;
  6. int& foo (void) { returng; }
  7. int& bar (void) { staticint s = 456; return s; }
  8. int& hum (void) { returnm; }
  9. Dummy& up (void) { ++m; return *this; }
  10. };
  11.  
  12. int& square (int x) {
  13. int* y = newint;
  14. *y = x * x;
  15. return *y;
  16. }
  17.  
  18. intconst& max (intconst& a, intconst& b)
  19. {
  20. return a > b ? a : b;
  21. }
  22.  
  23. int& foo (void)
  24. {
  25. int n = 123;
  26. return n;
  27. }
  28.  
  29. int main(int argc, constchar * argv[])
  30. {
  31.  
  32. Dummy dummy = {789};
  33. int& rg = dummy.foo ();
  34. std::cout<<"rg = "<< rg <<std::endl;
  35. int& rs = dummy.bar ();
  36. std::cout<<"rs = "<< rs <<std::endl;
  37. int& rm = dummy.hum ();
  38. std::cout<<"rm = "<< rm <<std::endl;
  39.  
  40. dummy.up().up().up();
  41. std::cout<<"rm = "<< rm <<std::endl;
  42.  
  43. int& y = square(16);
  44. std::cout<< y <<std::endl; // 256
  45. delete&y;
  46.  
  47. int a = 123, b = 456;
  48. std::cout<<&a <<' '<<&b <<std::endl;
  49. intconst& c = max (a, b);
  50. std::cout<<&c <<" : "<< c <<std::endl;
  51.  
  52. int&z = foo();
  53. std::cout<<"z = "<< z <<std::endl;
  54.  
  55. return0;
  56. }

2 定义用户类并实例化为对象

2.1 问题

定义一个用户类User,该类包含两个私有的数据成员,它们是姓名name和年龄age;包含六个公有的成员函数,它们是带参构造函数User,设置姓名函数setName,获取姓名函数getName,设置年龄函数setAge,获取年龄函数getAge,自我介绍函数who。

2.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义用户类

代码如下:

  1. #include <iostream>
  2.  
  3. class User
  4. {
  5. private:
  6. std::string m_name;
  7. int m_age;
  8. public:
  9. User (std::stringconst& name, int age)
  10. {
  11. m_name = name;
  12. m_age = age;
  13. }
  14. void setName(std::stringconst& name)
  15. {
  16. m_name = name;
  17. }
  18. std::string getName()
  19. {
  20. returnm_name;
  21. }
  22. void setAge(const int& age)
  23. {
  24. m_age = age;
  25. }
  26. int getAge()
  27. {
  28. returnm_age;
  29. }
  30. void who (void);
  31. };
  32.  
  33. voidUser::who (void)
  34. {
  35. std::cout<<"我是"<<m_name<<",今年"<< m_age <<"岁。"<< std::endl;
  36. }

上述代码中,以下代码:

  1. class User

定义了一个类,其中class为关键字,表示开始定义一个类;User为标识符,表示类名。

上述代码中,以下代码:

  1. private:
  2. std::string m_name;
  3. int m_age;

定义了两个私有的数据成员,姓名m_name和年龄m_age。

上述代码中,以下代码:

  1. public:
  2. User (std::stringconst& name, int age)
  3. {
  4. m_name = name;
  5. m_age = age;
  6. }
  7. void setName(std::stringconst& name)
  8. {
  9. m_name = name;
  10. }
  11. std::string getName()
  12. {
  13. returnm_name;
  14. }
  15. void setAge(const int& age)
  16. {
  17. m_age = age;
  18. }
  19. int getAge()
  20. {
  21. returnm_age;
  22. }
  23. void who (void);

定义了五个成员函数,它们是带参构造函数User,设置姓名函数setName,获取姓名函数getName,设置年龄函数setAge,获取年龄函数getAge。

声明了一个成员函数,是自我介绍函数who。该函数的定义在类外实现,如以下代码所示:

  1. voidUser::who (void)
  2. {
  3. std::cout<<"我是"<<m_name<<",今年"<< m_age <<"岁。"<< std::endl;
  4. }

在类外定义函数时,注意使用类名加作用域限定符(User::)说明该函数属于哪一个类。

步骤二:将用户类实例化为对象

代码如下所示:

  1. #include <iostream>
  2.  
  3. class User
  4. {
  5. private:
  6. std::string m_name;
  7. int m_age;
  8. public:
  9. User (std::stringconst& name, int age)
  10. {
  11. m_name = name;
  12. m_age = age;
  13. }
  14. void setName(std::stringconst& name)
  15. {
  16. m_name = name;
  17. }
  18. std::string getName()
  19. {
  20. returnm_name;
  21. }
  22. void setAge(constint& age)
  23. {
  24. m_age = age;
  25. }
  26. int getAge()
  27. {
  28. returnm_age;
  29. }
  30. void who (void);
  31. };
  32.  
  33. voidUser::who (void)
  34. {
  35. std::cout<<"我是"<<m_name<<",今年"<< m_age <<"岁。"<< std::endl;
  36. }
  37.  
  38. intmain(int argc, constchar * argv[])
  39. {
  40.  
  41.  
  42. User user("张飞", 25);
  43. user.who();
  44.  
  45. user.setName("关羽");
  46. user.setAge(28);
  47. user.who();
  48.  
  49. return0;
  50. }

上述代码中,以下代码:

  1. User user("张飞", 25);

定义了一个用户类User的对象user。类是用户自定义的一种数据类型,所以可以直接用类名来定义对象。对象名user后面的括号中的内容为类User的构造函数的实参,当为对象user分配完存储空间后将自动调用类的构造函数。

上述代码中,以下代码:

  1. user.who();
  2.  
  3. user.setName("关羽");
  4. user.setAge(28);
  5. user.who();

是调用类的成员函数。

2.3 完整代码

本案例的完整代码如下所示:

  1. #include <iostream>
  2.  
  3. class User
  4. {
  5. private:
  6. std::string m_name;
  7. int m_age;
  8. public:
  9. User (std::stringconst& name, int age)
  10. {
  11. m_name = name;
  12. m_age = age;
  13. }
  14. void setName(std::stringconst& name)
  15. {
  16. m_name = name;
  17. }
  18. std::string getName()
  19. {
  20. returnm_name;
  21. }
  22. void setAge(const int& age)
  23. {
  24. m_age = age;
  25. }
  26. int getAge()
  27. {
  28. returnm_age;
  29. }
  30. void who (void);
  31. };
  32.  
  33. voidUser::who (void)
  34. {
  35. std::cout<<"我是"<<m_name<<",今年"<< m_age <<"岁。"<< std::endl;
  36. }
  37.  
  38. intmain(int argc, constchar * argv[])
  39. {
  40.  
  41.  
  42. User user("张飞", 25);
  43. user.who();
  44.  
  45. user.setName("关羽");
  46. user.setAge(28);
  47. user.who();
  48.  
  49. return0;
  50. }