C++ Primer Plus读书笔记 —— 12 .1.2.3 类和动态内存管理

静态成员变量的声明

静态类成员变量是独立于类的对象而存储的。
声明时,须指明类型,使用作用域运算符,不使用关键字static
例如:int Class_Name::x = 1;
其次,初始化是在实现文件中,而不是在头文件中,否则可能会多次初始化。
一种例外情况是,静态数据成员为const类型枚举类型

c++自动提供的成员函数

默认构造函数
默认析构函数
复制构造函数
赋值运算符

何时需要自己定义上面的由编译器自动提供的成员函数?

  • ① 需要更新静态的类成员。(比如,说每生成一个对象,int count自增,每一个对象的生命周期结束,count自减)
  • ② 有需要动态申请内存的时候(使用newmalloc

默认构造函数

如果需要在新建一个对象的时候申请新的内存空间,必须显示的定义一个构造函数

默认析构函数

对于在新建对象时候申请的空间,必须在析构函数中释放。

复制构造函数

作用:新建一个对象并且初始化为现有对象,不过是浅复制,或者说仅仅是值的复制。
何时调用:类对象作函数参数、函数的返回值等等。
默认(隐式)构造函数的潜在问题:① 不会更新静态变量 ② 复制的仅仅是值,如果是动态申请的内存,同一块空间会被释放两次。
函数原型:ClassName(const ClassName &);

赋值运算符

作用:将一个已有的对象赋值给另一个对象(但不是一个为初始化的对象);
默认的赋值运算符的潜在问题:与复制构造函数类似。
函数原型:ClassName & ClassName::operator=(const ClassName &);
解决方案:
定义一个显示的赋值运算符定义,与显示定义的复制构造函数类似。
另外须注意:

    • 先用delete释放掉目标对象之前控制的某些内存;
    • 避免赋值给自己,一是没有必要,二是释放内存操作可能造成数据的破坏;
    • 返回一个对象的引用,方便连续赋值(return *this;

关于newdelete

  • 如果在构造函数中使用new来初始化成员指针,必须在析构函数中使用delete
  • newdeletenew[]delete[]必须相互匹配。
    举一个经典了例子;
    如果用字符数组去实现一个自己的String类,那么,在一般情况下需要开辟多个字符空间,但是如果仅仅需要开辟一个一个字符空间的话,也必须使用new char[1]
  • 如果有多个构造函数,则必须使用相同的方式使用new,要么都带,要么都不带。

String

下面是按照书上的String类,自己对照的写的,很有价值。

头文件

#ifndef MY_STRING_STRING_H
#define MY_STRING_STRING_H

#include <iostream>
using std::istream;
using std::ostream;

class String {
    
    
private:
    char* str;
    int len;
    static int numOfStrings;
    static const int CINLIM = 80;
public:
    String(); //默认构函数
    String(const char*); //构造函数
    String(const String&); //复制构造函数
    ~String();
    int length() const {
    
     return len;}

    String & operator=(const String&);  //用同类对象进行赋值
    String & operator=(const char*);    //用字面字符串赋值
    char & operator[](int);             //中括号运算符,返回引用是为了能够改变对应的字符值
    const char operator[](int) const ; //对于常量对象,上面的方法不能保证对象不被修改,所以必须重载

    friend bool operator<(const String&,const String&);  //之所有使用友元而不是成员函数来重载,是为了 "xxx" 和 String& 的比较。
    friend bool operator>(const String&,const String&);  //字面字符串指针能够调用复制构造函数生成一个临时的String对象。
    friend bool operator==(const String&,const String&);
    friend ostream & operator<<(ostream&,const String&); //返回引用一是为了减少开销
    friend istream & operator>>(istream&,String&); //二是因为iostream没有公有的复制构造函数

    static int how_many();

};

#endif //MY_STRING_STRING_H

实现文件

#include "String.h"

//类的静态成员只能在实现文件中初始化(除非是const static的),不在头文件中是防止多次包含头文件时多次初始化
int String::numOfStrings = 0;

int String::how_many() {
    
    
    return numOfStrings;
}

String::String() {
    
    
   len = 0;
   str = new char[1];  //不能是 new char;
   str[0] = '\0';
   ++numOfStrings;
}

String::String(const char *s) {
    
    
    len = strlen(s);
    str = new char[len+1];
    strcpy(str,s);
    ++numOfStrings;
}

String::String(const String &s) {
    
    
    len = s.len;
    str = new char[len+1];
    strcpy(str,s.str);
    ++numOfStrings;
}

String::~String() {
    
    
    --numOfStrings;
    delete [] str;
}

String &String::operator=(const String &s) {
    
    
    // 防止自己赋值给自己
    if(this==&s){
    
    
        return *this;
    }
    //将 str指向的堆空间中的字符串占的空间释放
    delete [] str;
    len = s.len;
    str = new char[len+1];
    strcpy(str,s.str);
    return *this;
}

String &String::operator=(const char *s) {
    
    
    delete [] str;
    len = strlen(s);
    str = new char[len+1];
    strcpy(str,s);
    return *this;
}

char &String::operator[](int pos) {
    
    
    return str[pos];
}

const char String::operator[](int pos) const {
    
    
    return str[pos];
}

bool operator<(const String &s1, const String &s2) {
    
    
    return strcmp(s1.str,s2.str)<0;
}

bool operator>(const String &s1, const String &s2) {
    
    
    return s1<s2;
}

bool operator==(const String &s1, const String &s2) {
    
    
    return !(s1<s2) && !(s2<s1);
}

ostream &operator<<(ostream &os, const String &s) {
    
    
    os<<s.str;
    return os;
}

istream &operator>>(istream &is, String &s) {
    
    
    char temp[String::CINLIM];
    is.get(temp,String::CINLIM);
    if(is) s = temp;
    return is;
}


猜你喜欢

转载自blog.csdn.net/qq_44846324/article/details/108297249