1. 类、对象、this指针
#include <iostream>
using namespace std;
//#define NAME_LEN 20
const int NAME_LEN = 20;
class CGoods//=>商品的抽象数据类型
{
public://给外部提供公有的方法,来访问私有的属性
//做商品数据初始化用的
//类体内实现的方法,自动处理成内联函数
void init(const char* name, double price, int amount); void show();
//给成员变量提供一个getXXX或setXXX的方法
void setName(const char* name)
{
strcpy(_name, name);
}
void setPrice(double price)
{
_price = price;
}
void setAmount(int amount)
{
_amount = amount;
}
const char* getName()
{
return _name;
}
double getPrice()
{
return _price;
}
int getAmount()
{
return _amount;
}
private://属性一般都是私有的
char _name[NAME_LEN];
double _price;
int _amount;
};
inline void CGoods::init(const char* name, double price, int amount)
{
strcpy(_name, name);
_price = price;
_amount = amount;
}
void CGoods::show()
{
cout << "_name:" << _name << endl;
cout << "price:" << _price << endl;
cout << "_amount:" << _amount << endl;
}
int main(void)
{
//CGoods可以定义无数的对象,每一个对象都有自己的成员变量,但是他们共享一套成员方法
//对象内存大小的时候 对象的内存大小只和成员变量有关
//show()=>怎么知道处理哪个对象的信息?
//init(name,price,amount)怎么知道把信息初始化给哪一个对象的?
//init(&good,"面包",10.0,200)
//类的成员方法一经编译,所有的方法参数,都会加一个this指针,接收调用该方法的对象的地址 CGoods good;//类实例化了一个对象
CGoods good;//类实例化了一个对象
good.init("面包", 10.0, 200);
good.show();
good.setPrice(20.5);
good.setAmount(100);
good.show();
CGoods good2;//类实例化了一个对象
good2.init("空调", 10000.0, 50);
good2.show();
return 0;
}
2. 构造函数与析构函数
#include <iostream>
using namespace std;
//OOP实现一个顺序栈
//构造函数和析构函数
class SeqStack
{
public:
//构造函数
SeqStack(int size = 10)//是可以带参数的,因此可以提供多个构造函数的重载
{
cout << this << " SeqStack" << endl;
_pstack = new int[size];
_top = -1;
_size = size;
}
//析构函数
~SeqStack()//是不带参数的,所以析构函数只能有一个
{
cout << this << " ~SeqStack" << endl;
delete[]_pstack;
_pstack = nullptr;
}
void push(int val)
{
if (full())
resize();
_pstack[++_top] = val;
}
void pop()
{
if (empty())
return;
--_top;
}
int top()
{
return _pstack[_top];
}
bool empty()
{
return _top == -1;
}
bool full()
{
return _top == _size - 1;
}
private:
int* _pstack;//动态开辟数组,存储栈的元素
int _top;//指向栈顶元素的位置
int _size;//数组扩容的总大小
void resize()//按原来容量的二倍进行扩容
{
int* ptmp = new int[_size * 2];//先分配扩容后的内存
for (int i = 0; i < _size; ++i)
{
ptmp[i] = _pstack[i];//把原来的东西拷贝给我新的内存//为什么不能用memcpy,realloc
}
delete[]_pstack;//删除原来的空间
_pstack = ptmp;
_size *= 2;
}
};
int main(void)
{
//1.开辟内存 2.调用构造函数
SeqStack s;//
//s.init(5);//对象成员的初始化操作
for (int i = 0; i < 15; ++i)
{
s.push(rand() % 10);
}
while (!s.empty())
{
cout << s.top() << " " << endl;
s.pop();
}
s.~SeqStack();//析构函数调用以后,我们说对象不存在了
s.pop();
SeqStack* ps = new SeqStack(60);// malloc SeqStack(60)
ps->push(70);
ps->push(80);
ps->top();
cout << ps->top() << endl;
delete ps;//先调用ps->~SeqStack()+然后free(ps) delete和free的区别? delete释放对象的内存并且调用析构函数,free只释放对象的堆内存
//s.release();//释放外部成员的堆内存
return 0;
}
3. 深拷贝和浅拷贝(普通赋值与赋值重载)
#include <iostream>
using namespace std;
//对象的深拷贝和浅拷贝
//OOP实现一个顺序栈
//构造函数和析构函数
//this指针=》类-》很多对象 共享一套成员方法
//成员方法,方法的参数都会添加一个this指针
//构造函数:
// 定义对象时,自动调用的,可以重载的;构造完成,对象产生了
//析构函数:
// 不带参数,不能重载,只有一个析构函数;析构完成,对象就不存在了,内存还存在
//.data对象
//heap new delete
//stack
//对象的深拷贝和浅拷贝
class SeqStack
{
public:
//构造函数
SeqStack(int size = 10) :_top(-1), _pstack(new int[size]), _size(size)
{
}
//自定义拷贝构造函数《=对象的浅拷贝现在有问题了
SeqStack(const SeqStack& src)
{
cout << "SeqStack(const SeqStack& src)" << endl;
_pstack = new int[src._size];
for (int i = 0; i <= src._top; ++i)
{
_pstack[i] = src._pstack[i];
}
_top = src._top;
_size = src._size;
}
//析构函数
~SeqStack()//是不带参数的,所以析构函数只能有一个
{
cout << this << " ~SeqStack" << endl;
delete[]_pstack;
_pstack = nullptr;
}
//赋值重载函数 s1 = s1
void operator =(const SeqStack& src)
{
//防止自赋值
if (this == &src)
{
return;
}
//需要先释放当前对象占用的外部资源
delete[]_pstack;
_pstack = new int[src._size];
for (int i = 0; i <= src._top; ++i)
{
_pstack[i] = src._pstack[i];
}
_top = src._top;
_size = src._size;
}
void push(int val)
{
if (full())
resize();
_pstack[++_top] = val;
}
void pop()
{
if (empty())
return;
--_top;
}
int top()
{
return _pstack[_top];
}
bool empty()
{
return _top == -1;
}
bool full()
{
return _top == _size - 1;
}
private:
int* _pstack;//动态开辟数组,存储栈的元素
int _top;//指向栈顶元素的位置
int _size;//数组扩容的总大小
void resize()//按原来容量的二倍进行扩容
{
int* ptmp = new int[_size * 2];//先分配扩容后的内存
for (int i = 0; i < _size; ++i)
{
ptmp[i] = _pstack[i];//把原来的东西拷贝给我新的内存
}
delete[]_pstack;//删除原来的空间
_pstack = ptmp;
_size *= 2;
}
};
int main(void)
{
{
SeqStack s;//没有提供任何构造函数的时候,会为你生成默认构造和默认析构函数
SeqStack s1(10);
SeqStack s2 = s1;// 1.调用默认的拷贝构造函数,做的是内存的数据拷贝,关键是对象如果占用外部资源,那么浅拷贝就出现问题了
//SeqStack s2 = s1;// 2.
//s2.operator=(s1)
//void operator =(const SeqStack &src)
s2 = s1;//默认的赋值函数=》做直接的内存拷贝
}
return 0;
}
return _pstack[_top];
}
bool empty()
{
return _top == -1;
}
bool full()
{
return _top == _size - 1;
}
private:
int* _pstack;//动态开辟数组,存储栈的元素
int _top;//指向栈顶元素的位置
int _size;//数组扩容的总大小
void resize()//按原来容量的二倍进行扩容
{
int* ptmp = new int[_size * 2];//先分配扩容后的内存
for (int i = 0; i < _size; ++i)
{
ptmp[i] = _pstack[i];//把原来的东西拷贝给我新的内存
}
delete[]_pstack;//删除原来的空间
_pstack = ptmp;
_size *= 2;
}
};
4. 类和对象代码应用实践
(1)实现一个字符串
#include <iostream>
using namespace std;
class String
{
public:
String(const char* str = nullptr)
{
if (str != nullptr)
{
m_data = new char[strlen(str) + 1];
strcpy(this->m_data, str);
}
else
{
m_data = new char[1];
*m_data = '\0';
}
}//构造函数
String(const String& other)
{
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data, other.m_data);
}//拷贝构造函数
~String(void)
{
delete[]m_data;
m_data = nullptr;
}//析构函数
//String&是为了支持连续的operator=赋值操作
String& operator = (const String& other)
{
if (this == &other)
{
return *this;
}
delete[] m_data;
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data, other.m_data);
return *this;
}//赋值重载函数
private:
char* m_data{ nullptr };
};
int main(void)
{
//调用带const char*参数的构造函数
String str1;
String str2("hello");
String str3 = "world";
//调用拷贝构造函数
String str4 = str3;
String str5(str3);
//调用赋值重载函数
str3 = str1 = str2;
return 0;
}
(2)实现一个循环队列
#include <iostream>
using namespace std;
//循环队列 memcpy realloc
class Queue
{
public:
Queue(int size = 5)
{
_pQue = new int[size];
_front = _rear = 0;
_size = size;
}
/*Queue(const Queue&) = delete;
Queue& operator=(const Queue&) = delete;*/
Queue(const Queue& src)
{
_size = src._size;
_front = src._front;
_rear = src._rear;
_pQue = new int[_size];
for (int i = _front; i != _rear; i = (i + 1) % _size)
{
_pQue[i] = src._pQue[i];
}
}
Queue& operator=(const Queue& src)
{
if (this == &src)
{
return *this;
}
delete[]_pQue;
_size = src._size;
_front = src._front;
_rear = src._rear;
_pQue = new int[_size];
for (int i = _front; i != _rear; i = (i + 1) % _size)
{
_pQue[i] = src._pQue[i];
}
return *this;
}
~Queue(void)
{
delete[] _pQue;
_pQue = nullptr;
}
void push(int val)
{
if (full())
resize();
_pQue[_rear] = val;
_rear = (_rear + 1) % _size;
}
void pop()
{
if (empty())
return;
_front = (_front + 1) % _size;
}
int front()//获取队头元素
{
return _pQue[_front];
}
bool full()
{
return (_rear + 1) % _size == _front;
}
bool empty()
{
return _front == _rear;
}
void resize()
{
int* ptmp = new int[2 * _size];
int index = 0;
for (int i = _front; i != _rear; i = (i + 1) % _size)
{
ptmp[index++] = _pQue[i];
}
delete[] _pQue;
_pQue = ptmp;
_front = 0;
_rear = index;
_size *= 2;
}
private:
int* _pQue;//申请队列的数组空间
int _front;//指示队头的位置
int _rear;//指示队尾的位置
int _size;//队列扩容的总大小
};
int main(void)
{
Queue queue;
for (int i = 0; i < 20; ++i)
{
queue.push(rand() % 100);
}
while (!queue.empty())
{
cout << queue.front() << " ";
queue.pop();
}
cout << endl;
Queue queue1 = queue;
queue1 = queue;
return 0;
}
5. 初始化列表
可以指定当前对象成员变量的初始化方式,尤其是对于成员对象来说,你不这么写可能还会报错。
初始化列表与写到函数体里的区别:
写到初始化列表里面相当于int _amount = a;在定义的时候直接进行了初始化。
而把它写在构造函数的函数体里面相当于int _amount;amount = a;先定义一个_amount,然后再把a的值赋值给amount。
对于简单类型,打开反汇编可以看到它们的指令是一样的,对于成员对象,这样写相当于先定义了一个CDate _date,然后对_date里的成员一一赋值。因为我没有提供默认构造,所以就会报错。
6. 类的各种成员方法以及区别
类的各种成员-成员方法/变量
普通的成员方法 => 编译器会添加一个this形参变量(静态对象无法调用)
1.属于类的作用域
2.调用该方法时,需要依赖一个对象
3.可以任意访问对象的私有成员
static静态成员方法 =>编译器不会生成this形参
1.属于类的作用域
2.用类名作用域来调用方法
3.可以任意访问对象的私有成员,仅限于不依赖对象的成员(只能调用其他的static静态成员)
const常成员方法 =>const CGoods *this
1.属于类的作用域
2.调用依赖一个对象,普通对象或者常对象都可以调用
3.可以任意访问对象的私有成员,但是只能读不能写
常对象只能调用常成员方法,所以当我们写一个函数只有读操作的时候,最好将它实现成const成员方法
#include <iostream>
using namespace std;
//类的各种成员-成员方法/变量
// 普通成员方法=》编译器会添加一个this形参变量
// 1.属于类的作用域
// 2.调用该方法时,需要依赖一个对象(常对象是无法调用的 实参:const CGoods* 形参:CGoodds *this)
// 3.可以任意访问对象的私有成员变量
//
// static静态成员方法=》不会生成this形参
// 1.属于类的作用域
// 2.用类名作用域调用方法
// 3.可以任意访问对象的私有成员,仅限于不依赖对象的成员(只能调用其他的static成员)
//
// const 常成员方法-》const CGoods *this
// 1.属于类的作用域
// 2.调用依赖一个对象,普通对象或者常对象都可以
// 3.可以任意访问对象私有成员,但是只能读,不能写,也只能调用常方法
//日期类
class CDate
{
public:
CDate(int y, int m, int d)//自定义了一个构造函数,编译器就不会再产生默认构造了
{
_year = y;
_month = m;
_day = d;
}
void show() const
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
//构造函数的初始化列表
//CDate信息 CGoods商品信息的一部分 a part of...组合的关系
class CGoods
{
public:
CGoods(const char* n, int a, double p,int y, int m, int d) :_date(y, m,d)//1.构造函数的初始化列表
{
//2.当前类类型构造函数体
strcpy(_name, n);
_amount = a;
_price = p;
_count++;
}
//普通的成员方法
void show() //打印商品的私有的信息 CGoods *this
{
cout << "name:" << _name << endl;
cout << "amount:" << _amount << endl;
cout << "price:" << _price << endl;
_date.show();
}
//常成员方法 只要是只读操作的成员方法,一律实现成const常成员方法
void show() const//打印商品的私有的信息 CGoods const ***this
{
cout << "name:" << _name << endl;
cout << "amount:" << _amount << endl;
cout << "price:" << _price << endl;
_date.show();
}
//静态的成员方法
static void showCGoodsCount()//打印所有商品共享的信息 没有this指针的
{
cout << "所有商品的种类数量是:" << _count << endl;
//cout << "name:" << _name << endl;
}
private:
char _name[20];
int _amount;
double _price;
CDate _date;//成员对象 1.分配内存 2.调用构造函数
static int _count;//不属于对象,而是属于类级别的 声明 用来记录商品对象的总数量
};
//static成员变量一定要在类外进行定义并且初始化
int CGoods::_count = 0;
int main(void)
{
CGoods good1("商品1",100,35.0,2019,5,12);
good1.show();
CGoods good2("商品2", 100, 35.0, 2019, 5, 12);
good2.show();
CGoods good3("商品3", 100, 35.0, 2019, 5, 12);
good3.show();
CGoods good4("商品4", 100, 35.0, 2019, 5, 12);
good4.show();
//good4.showCGoodsCount();
CGoods::showCGoodsCount();
const CGoods good5("非卖品", 100, 35.0, 2019, 5, 12);
good5.show();//CGoods::show(&good5) const CGoods***->const CGoods*this
return 0;
}
7. 指向类成员的指针
#include <iostream>
using namespace std;
//指向类成员(成员变量和成员方法)的指针
class Test
{
public:
void func()
{
cout << "call Test::func" << endl;
}
static void static_func()
{
cout << "Test::static_func" << endl;
}
int ma;
static int mb;
};
int Test::mb;
int main(void)
{
Test t1;
Test* t2 = new Test();
//指向成员方法的指针
void (Test:: * pfunc)() = &Test::func;
(t1.*pfunc)();
(t2->*pfunc)();
//如何定义函数指针指向类的static成员方法呢?
void (*pstatic_func)() = &Test::static_func;
(*pstatic_func)();
(*pstatic_func)();
#if 0
//int a = 10; int *p = &a; *p = 30;
//无法从“int Test::*转换为int *”
int Test::* p = &Test::ma;
t1.*p = 20;
cout << t1.*p << endl;
t2->*p = 30;
cout << t2->*p << endl;
int* p1 = &Test::mb;
*p1 = 40;
cout << *p1 << endl;
#endif
delete t2;
return 0;
}