C++底子精讲-07

打印 上一主题 下一主题

主题 1868|帖子 1868|积分 5604


1. const对象

1.概念:在 C++ 中,const 用于定义常量对象,一旦对象被初始化,其值就不能再改变。因为 const 对象只能被创建、撤销和只读访问,写操作是不允许的。
2.根本利用
  1. #include <iostream>
  2. #include <cstring>
  3. namespace myspace1
  4. {
  5.     using std::endl;
  6.     using std::cout;
  7.     using std::cin;
  8.     using std::string;
  9. }
  10. using namespace myspace1;
  11. class Person
  12. {
  13. public:
  14.     Person() :_age(0), _name(new char[strlen("张三") + 1])
  15.     {
  16.         cout << "无参构造" << endl;
  17.         strcpy(_name, "张三");
  18.     }
  19.     Person(int age,  const char* name) :_age(age), _name(new char[strlen(name) + 1])
  20.     {
  21.         cout << "有参构造" << endl;
  22.         strcpy(_name, name);
  23.     }
  24.     void setAge(int age)
  25.     {
  26.         _age = age;
  27.     }
  28.     void print()
  29.     {
  30.         cout << "age=" << _age << endl;
  31.         cout << "name=" << _name << endl;
  32.     }
  33.     //const成员函数,不能修改对象的数据成员
  34.     void print() const
  35.     {
  36.         cout << "age=" << _age << endl;
  37.         cout << "name=" << _name << endl;
  38.     }
  39.     ~Person()
  40.     {
  41.         delete[] _name;
  42.         cout << "析构函数" << endl;
  43.     }
  44. private:
  45.     int _age;
  46.     char* _name;
  47. };
  48. int main(void)
  49. {
  50.     //普通对象
  51.     Person p1(1,"张大");
  52.     p1.print();
  53.     p1.setAge(10);
  54.     p1.print();
  55.     //const 对象只能被创建、撤销和只读访问,写操作是不允许的
  56.     const Person p2(2, "张二");
  57.     //p2.setAge(20)   //不允许
  58.     //p2.print();     //不允许
  59.     p2.print();        //调用的是void print() const
  60.     return 0;
  61. }
复制代码

3.总结
const对象与const成员函数的规则:
(1)当类中有const成员函数和非const成员函数重载时,const对象会调用const成员函数,非const对象会调用非const成员函数;
(2)当类中只有一个const成员函数时,无论const对象还黑白const对象都可以调用这个版本;
(3)当类中只有一个非const成员函数时,const对象就不能调用非const版本。
发起:如果一个成员函数中确定不会修改数据成员,就把它定义为const成员函数。
2. 指向对象的指针

  1. int main(void)
  2. {
  3.     //在栈上开辟空间
  4.     Person p1(1, "一号");
  5.     Person* p2 = &p1;  //指向p1的指针;
  6.     Person* p3 = nullptr;  //空指针;
  7.     //在堆上开辟空间
  8.     Person* p4 = new Person(2, "二号");
  9.     delete p4;
  10.     p4 = nullptr;
  11.     Person* p5 = new Person();
  12.     p5->setAge(20);  
  13.     p5->print();
  14.     (*p5).setAge(30);
  15.     (*p5).print();
  16.     delete p5;
  17.     p5 = nullptr;
  18.     return 0;
  19. }
复制代码
3. 对象数组

  1. #include <iostream>
  2. #include <cstring>
  3. namespace myspace1
  4. {
  5.     using std::endl;
  6.     using std::cout;
  7.     using std::cin;
  8.     using std::string;
  9. }
  10. using namespace myspace1;
  11. class Person
  12. {
  13. public:
  14.     Person() :_age(0), _name(new char[strlen("张三") + 1])
  15.     {
  16.         cout << "无参构造" << endl;
  17.         strcpy(_name, "张三");
  18.     }
  19.     Person(int age,  const char* name) :_age(age), _name(new char[strlen(name) + 1])
  20.     {
  21.         cout << "有参构造" << endl;
  22.         strcpy(_name, name);
  23.     }
  24.     void setAge(int age)
  25.     {
  26.         _age = age;
  27.     }
  28.     void print()
  29.     {
  30.         cout << "age=" << _age << endl;
  31.         cout << "name=" << _name << endl;
  32.     }
  33.     //const成员函数,不能修改对象的数据成员
  34.     void print() const
  35.     {
  36.         cout << "age=" << _age << endl;
  37.         cout << "name=" << _name << endl;
  38.     }
  39.     ~Person()
  40.     {
  41.         delete[] _name;
  42.         cout << "析构函数" << endl;
  43.     }
  44. public:
  45.     int _age;
  46.     char* _name;
  47. };
  48. int main(void)
  49. {
  50.     // 方式一:使用无参/默认构造函数创建对象数组
  51.     cout << "使用默认构造函数创建对象数组:" << endl;
  52.     Person p1[3];
  53.     for (int i = 0; i < 3; i++) {
  54.         p1[i].print();
  55.     }
  56.     // 方式二:使用有参构造函数初始化对象数组
  57.     cout << "使用有参构造函数初始化对象数组:" << endl;
  58.     Person p2[3] = {
  59.         Person(20, "李四"),
  60.         Person(21, "王五"),
  61.         Person(22, "赵六")
  62.     };
  63.     for (int i = 0; i < 3; ++i) {
  64.         p2[i].print();
  65.     }
  66.     // 方式三:动态分配对象数组
  67.     cout << "动态分配对象数组:" << endl;
  68.     Person* p3 = new Person[2];
  69.     p3[0].setAge(25);
  70.     strcpy(p3[0]._name, "孙七");
  71.     p3[1].setAge(26);
  72.     strcpy(p3[1]._name, "周八");
  73.     for (int i = 0; i < 2; ++i) {
  74.         p3[i].print();
  75.     }
  76.     // 释放动态分配的内存
  77.     delete[] p3;
  78.     return 0;
  79. }
复制代码
4. c++中const常见用法总结

4.1 修饰常量

  1. //只有读权限,没有写权限
  2. const int a = 10;    //==int const a=10;
  3. //a = 20;  不可以修改
复制代码
4.2 修饰指针

  1. int main(void)
  2. {
  3.    
  4.     int a = 10;
  5.     int b = 20;
  6.     //1.指向常量的指针
  7.     const int* p1 = &a;  //==int const* p1 = &a;
  8.     //*p1 = 100;   不可以为通过指针修改a的值;
  9.     p1 = &b;  //可以修改指针p1的指向
  10.     //2.常量指针;可以通过指针修改变量的值,但是指针的指向不可以变;
  11.     int* const p2 = &a;
  12.     *p2 = 20;
  13.     //p2 = &b;  错误
  14.     //3.指向常量的常量指针:指针的指向和所指向的值都不能改变
  15.     const int* const p3 = &a;
  16.     // *p3 = 20; // 错误,不能通过指针修改所指向的值
  17.    // p3 = &b; // 错误,不能改变指针的指向
  18.     return 0;
  19. }
复制代码
4.3 修饰函数参数

const 用于修饰函数参数时,能够包管在函数内部不会修改该参数的值。
  1. void fun(const int a)
  2. {
  3.     //a = 100;   错误
  4.     cout << "a=" <<a<< endl;
  5. }
  6. int main(void)
  7. {
  8.     int a = 10;
  9.     fun(a);
  10.     return 0;
  11. }
复制代码
4.4 修饰函数返回值

const 修饰函数返回值时,表明返回值是一个常量,不能被修改。
  1. //const 修饰函数返回值时,表明返回值是一个常量,不能被修改。
  2. int const fun()
  3. {
  4.    
  5.     int a = 10;
  6.     return a;           
  7. }
  8. int main(void)
  9. {
  10.     //fun() = 100;  错误,不可以修改
  11.     return 0;
  12. }
复制代码
4.5 修饰成员函数

const 修饰类的成员函数时,表明该成员函数不会修改对象的状态
  1. class MyClass {
  2. private:
  3.     int _value;
  4. public:
  5.     MyClass(int val) : _value(val) {}
  6.     // 常量成员函数
  7.     int getValue() const {
  8.         // value = 20; // 错误,不能在常量成员函数中修改成员变量
  9.         return _value;
  10.     }
  11. };
复制代码
4.6 const对象

const 对象是指那些一经创建,其状态(即成员变量的值)就不能被修改的对象
  1. class MyClass {
  2. public:
  3.     int _value;
  4.     MyClass(int v) : _value(v) {}
  5. };
  6. int main() {
  7.     const MyClass obj(10);
  8.     // obj.value = 20; // 错误,不能修改 const 对象的成员变量
  9.     cout << "Value: " << obj._value << endl;
  10.     return 0;
  11. }
复制代码
5. 赋值运算符函数(增补)

5.1 概念

  1. Point pt1(1, 2), pt2(3, 4);
  2. pt1 = pt2;//赋值操作
复制代码
在执行 pt1 = pt2; 该语句时, pt1 与 pt2 都存在,以是不存在对象的构造,这要与 Point pt2 =pt1; 语句区分开,这是差别的。以是当 = 作用于对象时,需要调用的是赋值运算符函数
格式:
  1. 类名& operator=(const 类名 &)
复制代码
  1. class Point {
  2. private:
  3.     int _ix;
  4.     int _iy;
  5. public:
  6.     Point(int x = 0, int y = 0) : _ix(x), _iy(y) {}
  7.     //默认赋值运算符重载函数
  8.     Point& operator=(const Point& rhs)
  9.     {
  10.         
  11.         _ix = rhs._ix;
  12.         _iy = rhs._iy;
  13.    
  14.         return *this;
  15.     }
  16.     void print() const {
  17.        cout << "(" << _ix << ", " << _iy << ")" <<endl;
  18.     }
  19. };
  20. int main() {
  21.     Point p1(1, 2);
  22.     Point p2(3, 4);
  23.     p2 = p1;
  24.     p2.print();
  25.     return 0;
  26. }
复制代码
5.2 默认赋值运算符函数局限

(1)当类中包含指针数据成员且该指针指向堆上分配的内存时,默认的赋值运算符函数(编译器主动生成的)就无法满意需求,这可能会导致浅拷贝题目,进而引发内存泄漏或悬空指针等错误。
(2)默认的赋值运算符执行的是浅拷贝,也就是只复制指针的值,而不复制指针所指向的内存。这会造成两个对象的指针成员指向同一块内存,当此中一个对象被销毁时,它会释放这块内存,而另一个对象的指针就会变成悬空指针。另外,如果两个对象都实验释放同一块内存,会导致重复释放,引发未定义行为。
  1. class Person
  2. {
  3. public:
  4.     //构造函数
  5.     Person(const int* age) :_age(new int(*age))
  6.     {
  7.         cout << "构造函数" << endl;
  8.     }
  9.     //打印函数
  10.     void print()const
  11.     {
  12.         cout << "age=" << *_age << endl;
  13.     }
  14.     //析构函数
  15.     ~Person()
  16.     {
  17.         delete _age;
  18.         cout << "析构函数" << endl;
  19.     }
  20. private:
  21.     int* _age;
  22. };
  23. int main(void)
  24. {
  25.     int age1 = 20;
  26.     int age2 = 30;
  27.     Person p1(&age1);
  28.     p1.print();
  29.     Person p2(&age2);
  30.     p2.print();
  31.     p2 = p1;
  32.     return 0;
  33. }
复制代码
题目分析:
当执行 p2 = p1; 时,会调用编译器主动生成的默认赋值运算符。默认赋值运算符执行的是浅拷贝,即只复制指针的值,而不复制指针所指向的内存。这会导致 p1 和 p2 的 _age 指针指向同一块内存。由于浅拷贝,当 p2 对象被销毁时,会释放 _age 所指向的内存。此时,p1 的 _age 指针就会变成悬空指针。当 p1 对象被销毁时,再次释放同一块内存,会导致重复释放,引发未定义行为。
5.3 解决办法

为相识决浅拷贝的题目,需要自定义赋值运算符函数,实现深拷贝。深拷贝会为新对象分配新的内存,并将原对象的数据复制到新的内存中。
  1. class Person
  2. {
  3. public:
  4.     //构造函数
  5.     Person(const int* age) :_age(new int(*age))
  6.     {
  7.         cout << "构造函数" << endl;
  8.     }
  9.     //打印函数
  10.     void print()const
  11.     {
  12.         cout << "age=" << *_age << endl;
  13.     }
  14.     //自定义赋值运算符函数
  15.     Person& operator=(const Person& rhs)
  16.     {
  17.         //自我赋值检查
  18.         if (this != &rhs) {
  19.             //释放当前_age空间;
  20.             delete _age;
  21.             _age = nullptr;
  22.             _age = new int(*rhs._age);
  23.         
  24.         }
  25.         return *this;
  26.     }
  27.     //析构函数
  28.     ~Person()
  29.     {
  30.         delete _age;
  31.         cout << "析构函数" << endl;
  32.     }
  33. private:
  34.     int* _age;
  35. };
  36. int main(void)
  37. {
  38.     int age1 = 20;
  39.     int age2 = 30;
  40.     Person p1(&age1);
  41.     p1.print();
  42.     Person p2(&age2);
  43.     p2.print();
  44.     p2 = p1;
  45.     return 0;
  46. }
复制代码
若你有自行定义某些特殊成员函数,编译器会为类主动生成以下 6 个默认函数:

  • 默认构造函数
  • 析构函数
  • 拷贝构造函数
  • 拷贝赋值运算符
  • 移动构造函数(C++11 及以后)
  • 移动赋值运算符(C++11 及以后)
注意拷贝构造函数、赋值运算符函数、析构函数,如果需要手动定义此中的一个,那么另外两个也需要手动定义。 三合成原则

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

怀念夏天

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表