类和动态内存分配、String类、对象指针

        目录

    • 类和动态内存分配
    • 改进后的新String类
    • 在构造函数中使用new时应注意的事项
    • 有关返回对象的说明
    • 使用指向对象的指针

 一、动态内存和类

  • 静态类成员有一个特点:无论创建了多少对象,程序都只创建一个静态类变量副本,即类的所有对象共享同一个静态成员。
  • 不能在类声明中初始化静态成员变量;对于静态类成员,可以在类声明之外使用单独的语句来进行初始化,这是因为静态类成员是单独存储的,而不是对象的组成部分

int StringBad::num_strings = 0; //初始化指出类型,并使用作用域运算符,但没有使用关键字static

ps:静态数据成员在类声明中声明,在包含类方法的文件中初始化。初始化时使用作用域运算符来指出静态类成员所属的类。但如果静态成员是const 整数类型或枚举型,则可以在类声明中初始化。

  1. 删除对象可以释放对象本身占用的内存,但并不能自动释放属于对象成员的指针指向的内存。在构造函数中使用new分配内存时,必须在相应的析构函数中使用delete来释放内存。如果使用new[ ](包括中括号)来分配内存,则应使用delete[ ](包括中括号)来释放内存。
  2. 特殊成员函数

C++自动提供以下函数(在没有定义的情况下):默认构造函数,默认析构函数、复制构造函数、赋值运算符、地址运算符;

        (1)默认构造函数:不接受任何参数也不执行任何操作,带参数的构造函数也可以是默认构造函数,只要所有参数都有默认值,但只能有一个默认构造函数。

        (2)类的复制构造函数:原型:Class_name(const Class name&);其接受一个指向类对象的常量引用作为参数。按值传递意味着创建原始变量的一个副本,当按值传递和返回对象时,都将调用复制构造函数。由于按值传递对象将调用复制构造函数,因此应该按引用传递对象。这样可以节省调用构造函数的时间以及存储新对象的空间。默认的复制构造函数逐个复制非静态成员(成员复制也称为浅复制),复制的是成员的值。深度复制:复制构造函数应当复制字符串并将副本的地址赋给str成员,而不是仅仅复制字符串地址。如:

StringBad::StringBad(const StringBad & st)

{

  num_strings++; //handle static member update

  len=st.len;  //same length

  str=new char [len+1];  //allot space

  std::strcpy(str,st.str); //copy string to new location

  cout<<num_strings<<”: \””<<str<<”\”object created\n”; //for your information

      }

      必须定义复制构造函数的原因在于,一些类成员是使用new初始化的、指向数据的指针,而不是数据本身。警告:如果类中包含了使用new初始化的指针成员,应当定义一个复制构造函数,以复制指向的数据,而不是指针,这被称为深度复制。

        (3)赋值运算符 原型: class_name & class_name::operator=(const class_name &);它接受并返回一个指向类对象的引用。对于由于默认赋值运算符不合适而导致的问题,解决办法时提供赋值运算符(进行深度复制)定义。其实现与复制构造函数类似,但也有一些差别。

      3. 由于目标对象可能引用了以前分配的数据,所以函数应使用delete[ ]来释放这些数据。

      4. 函数应当避免将对象赋给自身;否则,给对象重新赋值前,释放内存操作可能删除对象的内容。

      5. 函数返回一个指向调用对象的引用。

二、改进后的String类

   C++11提供了引入关键字nullptr,用于表示空指针。

  1. 使用中括号表示法访问字符

        可以使用方法operator[ ]()来重载该运算符,通常,二元C++运算符(带两个操作数)位于两个操作数之间,如2+5. 但对于中括号运算符,一个操作数位于第一个中括号的前面,另一个操作数位于两个中括号之间。

      2. 静态类成员函数

        可以将成员函数声明为静态的(函数声明必须包含关键字static,但如果函数定义是独立的则不能包含)。不能通过对象调用静态成员函数;如果静态成员函数是在公有部分声明的,则可以使用类名和作用域解析运算符来调用它。由于静态成员函数不与特定的对象相关联,因此只能使用静态数据成员。同样,也可以使用静态成员函数设置类级(classwide)标记,以控制某些类接口的行为,例如,类级标记可以控制显示类内容的方法所使用的格式。

三、在构造函数中使用new时应注意的事项

    1.在使用new初始化对象的指针成员时要特别小心!

        (1)如果在构造函数中使用new来初始化指针成员,则应在析构函数中使用delete。

        (2)new和delete必须相互兼容。new对应delete,new[ ]对应delete[ ]

    2. 若有多个构造函数,则必须以相同的方式使用 new,要么都带中括号,要么都不带(所有构造函数都必须与一个析构函数兼容)。可在一个构造函数中使用new初始化指针,而在另一个构造函数中将指针初始化为空(0或nullptr),这是因为delete(无论是否有中括号)都可用于空指针。

    3. 应定义一个复制构造函数,通过深度复制将一个对象初始化为另一个对象。

    4. 应定义一个赋值运算符,通过深度复制将一个对象复制给另一个对象。

例如:

 String & String::operator=(const String & st)

  {

     If(this == & st) //object assigned to itself

         return *this;

     delete [ ] str; //free old string

     len = st.len;

      str=new char [len + 1]; //get space for new string

      std::strcpy(str, st.str); //copy the string

      return * this; //return reference to invoking object

}

检查自我赋值的情况,释放成员指针以前指向的内存,复制数据而不仅仅是数据的地址,并返回一个指向调用对象的引用。

    5. 有关返回对象的说明

当成员函数或独立的函数返回对象几种方式:返回指向对象的引用、指向对象的const引用或const对象。

    (1)返回指向const对象的引用(提高效率):返回对象将调用复制构造函数,而返回引用不会;引用指向的对象应该在调用函数执行时存在。

    (2)返回指向非const对象的引用:两种情形,重载赋值运算符以及重载与cout一起使用的<<运算符。

    (3)返回对象:如果被返回的对象是被调用函数中的局部变量,则不应按引用方式返回它。

        总之,如果方法或函数要返回局部变量,则应返回对象,而不是指向对象的引用。在这种情况下,将使用复制构造函数来生成返回的对象。如果方法或函数要返回一个没有公有复制构造函数的类(如ostream类)的对象,它必须返回一个指向这种对象的引用。最后,有些方法和函数(如重载的赋值运算符)可以返回对象,也可以返回指向对象的引用,在这种情况下,应首选引用,因为其效率更高。

四、使用指向对象的指针

   1.使用对象指针需注意

        (1) 使用常规表示法来声明指向对象的指针:

                String * glamour;

        (2) 可以将指针初始化为指向已有的对象:

                String * first = &sayings[0];

        (3)可以使用new来初始化指针,这将创建一个新的对象:

                String * favorite = new String (sayings[choice]);

     2. 对类使用new将调用相应的类构造函数来初始化新创建的对象

        (1)可以使用->运算符通过指针访问类方法:

                If(sayings [i].length()< shortest->length());

        (2)可以对对象指针应用解除引用运算符(*)来获取对象。

     3. 析构函数调用时机

        (1) 如果对象是动态变量,则当执行完定义该对象的程序时,将调用该对象的析构函数。

        (2) 如果对象是静态变量(外部、静态、静态外部或来自名称空间),则在程序结束时将调用对象的析构函数。

        (3)如果对象是用new创建的,则仅当显式使用delete删除对象时,其析构函数才会被调用。

      4. 再谈定位new运算符

        如果使用定位new运算符来为对象分配内存,必须确保其析构函数被调用。delete可与常规new运算符配合使用,但不能与定位new运算符配合使用。解决方案是显式的为使用定位new运算符创建的对象调用析构函数。对于使用定位new创建的对象,应以与创建顺序相反的顺序进行删除。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++中,可以使用new来动态地在堆内存中分配对象。例如,用 new ClassName()语法实现从堆内存中分配ClassName对象,并将此对象的地址存储在ClassName *指针中。\[2\]这种方式可以在程序运行时动态地分配内存,而不是在编译时就确定内存大小。这对于需要根据运行时条件来确定内存大小的情况非常有用。 另外,在C++中,std::string也会动态地分配内存来存储字符串数据。当我们给std::string赋值一个较长的字符串时,如果当前分配的内存空间不足以容纳新的字符串,std::string会动态地分配更多的内存来存储新的字符串,并将原先的内容拷贝到新的内存空间中。\[3\]这样可以确保std::string能够容纳任意长度的字符串,但也会带来一定的性能开销。 总结起来,C++中的动态内存分配可以通过new关键字来实现对象的动态分配,而std::string则会在需要时动态地分配内存来存储字符串数据。这样可以灵活地管理内存,并确保能够容纳任意长度的字符串。 #### 引用[.reference_title] - *1* *3* [C++ string介绍和坑](https://blog.csdn.net/weixin_43679037/article/details/127536657)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [C++动态内存分配new](https://blog.csdn.net/qq_40965507/article/details/119383348)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhugenmi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值