类&对象(一)
来源:互联网 发布:剑网三琴萝捏脸数据 编辑:程序博客网 时间:2024/05/21 06:11
C++中定义一个类,本质上就是定义一个数据结构,以关键字class作为标志
一个典型的类结构定义如下:
class test{ public: int key1; int key2; void get_sum();};关键字class与struct不同的地方在于,struct默认第一个访问说明符之前的成员是public的,而class则使这些成员为private权限的。
定义类的时候,可以定义成员函数,可以在类里面声明并定义函数体,也可以在类外面使用范围解析运算符::进行说明,例如上述的成员函数可以说明如下:
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;}
- 类&对象(一)
- 对象和类(一)
- 类和对象(一)
- 类和对象(一)
- 类与对象(一)
- 一、对象导论(一)
- 第三章 面向对象(一)--类与对象
- C++面向对象(一)——类与对象
- 理解面向对象编程(一)-类和对象
- C#面向对象(一)封装、类和对象
- Python面向对象(一)对象和类
- 类对象的内存分配(一)
- 实验三 类与对象 (一)
- js自定义类和对象(一)
- LUA面向对象程序设计(一)类
- Java反射(一)Class类对象
- php类与对象(一)
- java复习-类和对象(一)
- C++Builder对C++的扩展
- ubuntu 环境下安装中文输入法
- Web项目开发流程 PC端
- MTU 和 MSS
- Linux帮助命令
- 类&对象(一)
- 独木舟上的旅行【贪心】
- Linux系统—tar 命令参数解析
- 让json懂中文
- vue.js在visual studio 2017下的安装
- 删除字符串指定字符
- vim的配置文件
- Android设计模式之MVP
- 堆排序模版