类&对象(一)

C++中定义一个类,本质上就是定义一个数据结构,以关键字class作为标志

一个典型的类结构定义如下:

class test
{
    public:
        int key1;
        int key2;
        void get_sum();
};

关键字class与struct不同的地方在于,struct默认第一个访问说明符之前的成员是public的,而class则使这些成员为private权限的。

C的struct和C++struct不同:c++struct可以有成员函数

定义类的时候,可以定义成员函数,可以在类里面声明并定义函数体,也可以在类外面使用范围解析运算符::进行说明,例如上述的成员函数可以说明如下:

void test::get_sum()
{
    return key1+key2;
}
定义类之后,就可以进行对象的声明以及访问了,如下:

    test s;
    s.key1=1;
    s.key2=2;
    cout<<s.get_sum();

(1)不要显式调用一个类的构造函数

(2)堆栈中声明结果是一个对象(.调用成员数据或者函数),在堆中声明返回的是一个指针(*().调用,等于->)

(3)堆栈中创建对象的时候,如果调用默认构造函数不需要(),使用后会当成一个函数声明;

但是,在堆中创建的时候,需要带上括号;

(4)显式的默认构造函数:

class_name() {}  或者  class_name()=default;

  (5)复制构造函数

使创建的对象是另一对象的精确副本,如果未编写,编译时会自动生成,使用的是对象的const引用,如下:

class class_name

{

public:

class_name(const class_name& class1);   

}

(6)初始化列表构造函数   #include<initializer_list> 

形如:class_name(initializer_list<T> args) {...}

例如:std::vector<std::string> myVec ={"String 1","String 2","String 3"};

以下几种情况时必须使用初始化列表:
常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。

(7)复制和赋值的区别

在对象声明的时候,使用的是复制构造函数,如:

class_name class1(1);

class_name class2(class1);  //使用复制构造函数 

class_name class3=class1; // 使用复制构造函数

已经构建的对象,会使用赋值,其实就是operator=的重载

class_name class4;

class4 = class1;  //使用赋值

(8)动态分配内存(并非真正意义的动态,只是在遇到不同的情况能够进行不同的处理,与vector中的动态扩展内存不是一个概念):有的时候,并不知道对象应该给多大的空间,只是给了一些参数,这个时候就要用动态分配:

例如,有一个点SpreadsheetCell类如下:

class SpreadsheetCell
{
public:
    void setValue(double inValue);
    double getValue() const;
private:
    double mValue;
};
现在需要创建一个类,里面可能包含多个点(每个点用两个数值坐标定位),但是具体多少个需要根据参数来决定,那么在进行构造的时候,就应当使用动态分配内存
class Spreadsheet
{
public:
    Spreadsheet(int inWidth,int inHeight);
    void setCellAt(int x,int y,const SpreadsheetCell& cell);
    SpreadsheetCell& getCellAt(int x,int y);
private:
    bool inRange(int val,int upper);
    int mWidth,mHeight;
    //因为数组尺度不同,为了动态分配,采用二维指针替代二维数组
    SpreadsheetCell** mCells;
};
//采用动态分配内存的构造函数
Spreadsheet::Spreadsheet(int inWidth,int inHeight):
    mWidth(inWidth),mHeight(inHeight)
{
    mcells= new SpreadsheetCell* [mWidth];//先分配二维指针
    for(int i=0;i<mWidth;i++)
    {
        mCells[i]=new SpreadsheetCell[mHeight];//每个一维指针指向一段连续内存的数组
    }
}

相应的,应该有一个动态删除的析构函数如下:

//采用动态删除的析构函数
Spreadsheet::~Spreadsheet()
{
    for(int i=0;i<mWidth;i++)
    {
        delete[] mCells[i];//后申请的先删除
    }
    delete [] mCells;
    mCells=nullptr;
}
(9)复制的理解:(编译生成的复制构造函数和赋值语句只是表层复制,对于动态分配内存不适用)

初始化一个对象:Spreadsheet s1(4,3);

某个函数调用:func(s1);

这个时候传参是表层复制,只是复制了一个mCells的副本,它与原来的mCells指向同一块地址;对该副本的操作会直接改变对象的内存情况,危险!!

还有一种情况:

Spreadsheet s1(2,2),s2(4,3);

s1=s2;

这样执行之后,s1的指针mCells直接指向s2的内存区域,那么s1本来申请的区域被遗弃!内存泄漏!!

所以:如果在类里面动态分配了内存,就需要自己重新写复制构造函数和赋值运算符,以便于深层的内存复制。

重新定义复制构造函数:

Spreadsheet::Spreadsheet(const Spreadsheet& src)
{
    mWidth = src.mWidth;
    mHeight = src.mHeight;
    mCells = new SpreadsheetCell*[mWidth];
    //先创建底层的内存
    for(int i=0;i<mWidth;i++)
    {
        mCells[i]= new SpreadsheetCell[mHeight];
    }
    //对底层的内存进行数值拷贝
    for(int i=0;i<mWidth;i++)
    {
        for(int j=0;j<mHeigth;j++)
        {
            mCells[i][j]=src.mCells[i][j];
        }
    }
}
重载赋值运算符:(因为在赋值的时候已经初始化了,故而需要先释放已分配的内存,否则内存泄漏)

Spreadsheet& Spreadsheet::operator=(const Spreadsheet& rhs)
{
    //自检测,为了赋值的正确性
    if(this == &rhs)
    {
        return *this;
    }
    //先删除已经分配的内存
    for(int i=0;i<mWidth;i++)
        delete[] mCells[i];
    delete[] mCells;
    mCells = nullptr;
    //进行拷贝
    mWidth = rhs.mWidth;
    mHeight = rhs.mHeight;
    mCells = new SpreadsheetCell*[mWidth];
    for(int i=0;i<mWidth;i++)
    {
        mCells[i]= new SpreadsheetCell[mHeight];
    }
    for(int i=0;i<mWidth;i++)
    {
        for(int j=0;j<mHeigth;j++)
        {
            mCells[i][j]=rhs.mCells[i][j];
        }
    }
    //该运算符有返回值
    return *this;
}

(10)   =default控制默认构造函数的生成,显式地指示编译器生成该函数的默认版本

 =delete显式指示编译器不生成函数的默认版本 


上一部分完整代码:

#include <iostream>
#include <vector>
#include <string>
#include<stdexcept>

using namespace std;

class SpreadsheetCell
{
public:
    void setValue(double inValue);
    double getValue() const;
private:
    double mValue;
};
void SpreadsheetCell::setValue(double inValue)
{
    mValue = inValue;
}
double SpreadsheetCell::getValue() const
{
    return mValue;
}

class Spreadsheet
{
public:
    Spreadsheet(int inWidth,int inHeight);
    Spreadsheet(const Spreadsheet& src);
    Spreadsheet& operator=(const Spreadsheet& rhs);
    void setCellAt(int x,int y,const SpreadsheetCell& cell);
    SpreadsheetCell& getCellAt(int x,int y);
    ~Spreadsheet();
private:
    bool inRange(int val,int upper);
    int mWidth,mHeight;
    //因为数组尺度不同,为了动态分配,采用二维指针替代二维数组
    SpreadsheetCell** mCells;
};
//采用动态分配内存的构造函数
Spreadsheet::Spreadsheet(int inWidth,int inHeight):
    mWidth(inWidth),mHeight(inHeight)
{
    mCells= new SpreadsheetCell* [mWidth];
    for(int i=0;i<mWidth;i++)
    {
        mCells[i]=new SpreadsheetCell[mHeight];
    }
}
//采用动态删除的析构函数
Spreadsheet::~Spreadsheet()
{
    for(int i=0;i<mWidth;i++)
    {
        delete[] mCells[i];//后申请的先删除
    }
    delete [] mCells;
    mCells=nullptr;
}

bool Spreadsheet::inRange(int val,int upper)
{
    if(val>0&&val<upper)
        return true;
    else
        return false;
}

void Spreadsheet::setCellAt(int x,int y,const SpreadsheetCell& cell)
{
    if(!inRange(x,mWidth)||!inRange(y,mHeight))
        throw std::out_of_range("");
    mCells[x][y]=cell;
}
SpreadsheetCell& Spreadsheet::getCellAt(int x,int y)
{
    if(!inRange(x,mWidth)||!inRange(y,mHeight))
        throw std::out_of_range("");
    return mCells[x][y];
}

Spreadsheet::Spreadsheet(const Spreadsheet& src)
{
    mWidth = src.mWidth;
    mHeight = src.mHeight;
    mCells = new SpreadsheetCell*[mWidth];
    //先创建底层的内存
    for(int i=0;i<mWidth;i++)
    {
        mCells[i]= new SpreadsheetCell[mHeight];
    }
    //对底层的内存进行数值拷贝
    for(int i=0;i<mWidth;i++)
    {
        for(int j=0;j<mHeight;j++)
        {
            mCells[i][j]=src.mCells[i][j];
        }
    }
}

Spreadsheet& Spreadsheet::operator=(const Spreadsheet& rhs)
{
    //自检测,为了赋值的正确性
    if(this == &rhs)
    {
        return *this;
    }
    //先删除已经分配的内存
    for(int i=0;i<mWidth;i++)
        delete[] mCells[i];
    delete[] mCells;
    mCells = nullptr;
    //进行拷贝
    mWidth = rhs.mWidth;
    mHeight = rhs.mHeight;
    mCells = new SpreadsheetCell*[mWidth];
    for(int i=0;i<mWidth;i++)
    {
        mCells[i]= new SpreadsheetCell[mHeight];
    }
    for(int i=0;i<mWidth;i++)
    {
        for(int j=0;j<mHeight;j++)
        {
            mCells[i][j]=rhs.mCells[i][j];
        }
    }
    //该运算符有返回值
    return *this;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值