C++创建一个类的注意事项

内存对齐

节省CPU在IO上的访问次数

从理论上讲,对任何变量的访问都可以从任何地址开始,
但是实际的计算机系统对基本类型数据在内存中的存放有限制,
他们会要求这个元素首地址的值是4/8的倍数,

大部分处理器并不是按字节块来存取内存的.
它一般会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存,我们将上述这些存取单位称为内存存取粒度,

如果没有内存对齐机制,数据可以任意存放,一个int变量存放在地址1开始的,连续的四个字节地址中,处理器获取数据的过程
1.从0地址开始读取第一个四字节块,删除不想要的字节0地址
2.然后从地址4开始读取下一个四字节块,删除不要的数据5,6,7的地址
3.剩下两块数据合并放入寄存器,需要完成很多工作

使用内存对齐后
int类型数据只能存放在按照对齐规则的内存中,处理器可以一次性将数据读出来,不需要额外操作,提高了效率。

C++修改对齐模数
gcc中默认的#pragma pack(4)
可以通过预编译命令#pragma pack(n)
有效对齐值:#pragma pack(n) 和结构体中最长数据类型中较小的那个,也叫做对其单位

第一个成员的偏移量(offset)为0,以后每个成员相对于首地址的offset都是该成员大小与有效对齐值中较小的那个的整数倍,如果有需要,编译器会在成员之间填充字节,
总大小为有效对齐值的整数倍,如果有需要编译器会在最末一个成员之后加上填充字节

如果类型大于4字节 按照四字节处理,如果小于4字节,就按照它的大小内存填充,如果结束地址是他类型大小的整数倍就不需要填充。

.和->的区别

类和对象、this指针

this 指针用来区分调用该方法的不同对象


/*
	this指针,一个类产生的多个对象 每个对象都有自己的一份成员变量 同一个类型的对象共享一套方法
				通过this指针区分我当前到底是在操作那个对象的成员
	
	成员方法一经编译,都会添加一个this指针,用来接收调用该方法的对象

	构造函数:1.定义对象时自动调用;2.可以重载;3.构造完成,对象产生了
	析构函数:1.不带参数,不能重载,只有一个析构函数;2.析构完成,对象就不存在了
		.data段对象	程序启动时构造,程序结束时析构
		heap new的时候构造,delete的时候析构
		stack 进入函数定义的地方构造,出了函数析构

*/
#include<iostream>
using namespace std;

/*
C++		:	OOP编程,this指针,
C		:	各种各样的函数的定义,Struct
C++类	:	实体的抽象类型,

实体分析属性和行为两方面得到  ->		ADT(abstract data type)抽象的数据类型	属性=成员变量	行为=成员方法
								类实例化的对象要占用内存空间,类实例化后的对象才代表实体
*/

//OOP语言的四大特征是什么 抽象 封装/隐藏 继承 多态
//通过访问限定符体现出封装特性	公有的public/私有的private/保护的protected
//定义成员方法可以在	1.类内定义 自动处理成内联函数
//					2.类外定义 需要在类方法前加上类的作用域 并且不自动处理成内联函数

const int NAME_LEN = 20;
//类型是不占空间的,类型定义的对象需要占内+存空间,对象的内存都在栈上
class CGoods	//类名以一个大写的C开始,表示这是一个类类型 类名每个单词都是大写 (商品的抽象数据类型)
{
//成员方法和成员变量都是第一个单词第一个字母小写,后面的首字母大写
//给外部提供公有的成员方法,访问私有的属性
//成员方法是对实体行为的输出,不指定对象光说行为毫无意义。成员方法的访问一定依赖对象,
public:
	//类的成员方法在编译器编译之后,给所用的成员方法参数添加一个this指针,接受调用该方法对象的地址
	//多个对象可以调用一套方法的本质
	//void init(CGoods *this, const char* name, double price, int amount);
	void init(const char* name, double price, int amount);//初始化函数 接受外部传入的变量初始化私有成员变量	做类初始化用
	void show();	//打印商品信息
	//给成员提供getXXX/setXXX的方法
	void setName(char* name)
	{
		strcpy(_name, name);
	}
	void setPrice(double price)
	{
		_price = price;
	}
	void setAmount(int amount);

	//	char* getName();	如果返回值是一个char* 那么外面接受到的是私有成员变量地址,就可以间接修改成员变量
	//类体内实现的成员方法 自动处理成inline内联函数
	const char* getName()
	{
		return _name;
	}
	double getPrice()
	{
		return _price;
	}
	int getAmount();


//输出私有成员变量,也就是属性,属性一般都是私有的 可以使用方法请求调用
private:
	char _name[NAME_LEN];	//成员变量一般以小写m或者下划线开头,区分和其他变量 常量可以做数组下标,变量不能做数组下标
	double _price;
	int _amount;
};

//类外定义成员方法 必须在类方法前面加上类的作用域,不加作用域等同于全局方法
//类外定义成员方法	不自动处理成内联函数,如果与内联函数需要在函数前面加上inline关键字
void CGoods::init(const char* name, double price, int amount)
{
	//编译的之后给成员变量添加一个this指针 通过this指针区分调用该方法的不同对象
	strcpy(_name, name);
	//this->_price = price 访问this指针指向对象中的成员方法
	_price = price;
	_amount = amount;
	
}

void CGoods::show()
{
	cout << "name:" << _name << endl;
	cout << "price:" << _price << endl;
	cout << "amount:" << _amount << endl;
}

void CGoods::setAmount(int amount)
{
	_amount = amount;
}
int main()
{
	//对象的内存大小只和成员变量有关和成员方法无关
	//可以使用工具查看代码,使用vs控制台,切换到代码目录,是用cl 目录.cpp /dlreportSingleClassLayoutCGood
	/*
		类型可以定义无数的对象,每一个对象都有自己的成员变量,但是他们共享一套成员方法
		方法编译成指令要放在代码段上,

		问题,成员方法是怎么知道处理那个对象的信息呢?

	*/
	CGoods good;			//类实例化了一个对象

	//常量字符串,地址不能泄露给普通指针,解引用可以修改常量字符串的值,这在编译器中是不允许发生的

	//实参是一个地址类类型,所以形参需要用一个指针来接受
	//init(&good1,"面包",10,200) 把调用方法对象的地址传入此处
	good.init("海豹", 20, 5);
	//show(&good1)
	good.show();
	good.setPrice(100);
	good.setAmount(1);
	good.show();

	CGoods good1;
	good1.init("火豹豹", 200, 1000);
	good1.show();
	good1.setPrice(20);
	good1.setAmount(999);
	good1.show();
	
	return 0;
}

构造函数和析构函数

#include<iostream>
using namespace std;

/*
		构造函数和析构函数
		1.函数名和类名保持一致
		2.没有返回值
		3.先构造的后析构,后构造的先析构
		4.先开辟内存,内存有了,成员变量就有了,但是他们的值还不合法,,调用构造函数,所有成员合法初始化了
		OOP实现一个顺序栈~
*/

class SeqStack
{
//方法一般写成公有的
public:
		//init给栈初始化
		void init(int size = 10)	//带形参默认值的函数
		{
			//类定义的对象占12个字节,指针指向了外部堆上的一块内存
			//需要在使用完后释放掉在堆区开辟的内存
			_pstack = new int[size];
			_top = -1;
			_size = size;
		}

		//构造函数
		//可以带参数,所以可以提供多个构造函数
		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 release()
		{
			//使用完释放栈上内存,并且使用nullptr使其不成为野指针
			//指向数组内存,中间要加上中括号
			delete[]_pstack;
			_pstack = nullptr;
		}
		//入栈
		void push(int value)
		{
			if (full())
				resize();
			_pstack[++_top] = value;	//初始化top为-1 所以需要先++然后给数组首元素赋值
		}
		//出栈
		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];
		//为什么不用memcpy(ptmp,_pstack,sizeof(int)*_size)
		//为什么不用realloc进行扩容
		//他们都属于内存拷贝,但是在面向对象里面不适合。(深拷贝,浅拷贝)
		for (int i = 0; i < _size; ++i)
		{
			ptmp[i] = _pstack[i];
		}
		delete[] _pstack;
		_pstack = ptmp;
		_size *= 2;
	}
};

int main()
{
	//1.开辟内存
	//2.调用构造函数
	//在栈上开辟对象
	SeqStack s;
	s.init(5);
	//在堆上开辟内存
	//堆上的对象必须手动析构,在调用delete前不会自动析构
	//delete 
	//1.释放这块内存之前,首先调用这块内存这个对象的析构函数,把这个对象函数占用的外面的堆内存释放掉
	//2.然后释放这个对象本身的堆内存
	//delete = ps->~类名() + free(类名)
	//new =  malloc + 类名()
	SeqStack* ps = new SeqStack(60);
	ps->push(70);

	for (int i = 0; i < 15; ++i)
	{
		s.push(rand() % 100);
	}

	while (!s.empty())
	{
		cout << s.top() << " ";
		s.pop();
	}
	//问题出现了 忘记了释放资源 光开辟了
	s.release();

	//包含初始化和资源释放的函数都需要手动调用
	
	//栈上对象 在return处依次析构 出了作用域
	return 0;
}

深拷贝和浅拷贝

#include<iostream>
using namespace std;

/*
	深拷贝和浅拷贝

	对象默认的拷贝是做内存数据的拷贝

	浅拷贝:对象直接进入内存拷贝,对象浅拷贝不一定有错,如果有指向对象之外的外部资源则出错(占用外部资源)

	深拷贝:每个成员有自己独有的外部资源	
*/

class SeqStack
{
public:
	SeqStack(int value)
	{
		_value = value;
	}
	//自定义拷贝构造函数	对象的浅拷贝会出现问题
	SeqStack(const SeqStack& src)
	{
		//默认的浅拷贝
		_value = src._value;
		_pstack = src._pstack;
		_size = src._size;
		_top = src._top;
		//自己构造成深拷贝
		_value = src._value;
		_pstack = new int[src._size];
		for (int i = 0; i <= src._top; ++i)
		{
			_pstack[i] = src._pstack[i];
		}
		_size = src._size;
		_top = src._top;
		/*
			为什么使用for循环为不使用memcpy内存拷贝 
			在进行数据拷贝的过程中,如果只是整形,他就占用一个整形空间,不占用类型所占用类型大小内存之外的资源
			出现为标题的情况,拷贝对象或者指针,指向了外部的资源,浅拷贝会出现问题,拷贝了对象本身占用的内存而已,浅拷贝特征。
			是的memcpy拷贝的指针和原对象指向了同一块资源,外部资源会释放两次。
			唯一可以使用的情况,所拷贝对象明确得知没有占用外部资源。(浅拷贝不会出错的环接可以直接使用内存拷贝)
		*/
	}

	//赋值重载函数
	void operator= (const SeqStack& src)
	{
		//和拷贝构造发生的一样,只不过要释放掉src指针原来指向的内存
		//需要释放当前对象占用的外部资源

		//第一步防止自赋值
		if (this == &src)
		{
			return;
		}
		//第二步,释放当前对象占用的外部资源
		delete[] _pstack;

		//第三步,for循环赋值
	
	}

private:
	int* _pstack;
	int _value;
	int _size;
	int _top;
};
int main()
{
	SeqStack s0;			//没有提供任何构造函数,会生成默认构造和默认析构,不带参数的构造,是空函数
							//当你提供构造函数的时候,就不会为你提供默认构造
	
	SeqStack s1(10);		//调用了一个带整形参数的构造函数
							//以下两者等效 调用拷贝构造函数
							//默认拷贝构造,用一个同类型的已经存在的对象,来产生一个新对象
							//默认拷贝构造,内存拷贝,把所有成员变量的值赋值给新的对象
	
	SeqStack s2 = s1;		//对象的生成一定会调用构造 
	SeqStack s3(s1);

	//s2.operator=(s1)
	//void operator = (const SeqStack &src) 赋值函数
	s2 = s1;				//这是一个赋值操作,这不是初始化,不会生成新的对象,都是已经存在的对象。
							//默认的内存拷贝
							//又是浅拷贝的问题,还丢失了s2指针所指向的内存,无法释放,找不到了
	s1 = s1;				//自己释放自己,内存非法访问,消除
	return 0;
}

String简易模拟

#include<iostream>
using namespace std;


//这个类产生的对象就占用四个字节 因为这个类的成员变量是一个指针,要占用外部的堆内存,默认发生浅拷贝,需要重写拷贝构造和赋值
class String
{
public:
	//构造函数 NULL容易和0意义混淆,可以给整数赋值NULL 无法区分指针和整数
	//字符串处理,不要有些时候让其有效,有些时候让其置空, 直接排除空的可能性
	String(const char* str = nullptr)
	{
		if (str != nullptr)
		{
			m_data = new char[strlen(str) + 1];	//+1 包含了str中'\0'的一个字节
			strcpy(m_data, str);
		}
		else
		{	
			//为了避免每次进入其他函数都需要进行判空操作
			m_data = new char[1];
			//为什么不使用new char 创建字符串数组
			*m_data = '\0';	
		}

	}
	//析构函数
	~String()
	{
		delete[] m_data;
		//防止野指针的出现
		m_data = nullptr;
	}
	//拷贝构造函数
	String(const String& others)
	{
		//由于构造函数保证了String对象一定是一个有效的对象,所以不需要判断空
		m_data = new char[strlen(others.m_data) + 1];
		strcpy(m_data, others.m_data);
	}
	//复制重载函数 返回值会产生临时对象
	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;
};

int main()
{
	//默认构造,使用了nullptr
	String str1;

	//带有char*参数的构造函数
	String str2("hello");
	String str3 = "world";

	//初始化,赋值是左边的对象已经存在,初始化是左边的对象正在构造过程中
	String str4 = str3;
	String str5(str3);

	//赋值 等号左右两边的对象都存在
	//调用复制重载函数
	str1 = str3;

	//赋值是从右向左赋值的
	// str2 = str1 str2.operator=(str2)
	//如果返回值为void 连续赋值就会出现 str3 = void 的奇异景象
	//返回值是当前类型的引用,作用是为了支持连续赋值
	str3 = str2 = str1;
}

Queue简易模拟

#include<iostream>
using namespace std;

//循环队列 mencpy/realloc做的是内存的拷贝,不适合用于对象本身拷贝,对象占用外部资源
class Queue
{
public:
	Queue(int size = 5)
	{
		_pQue = new int[size];
		_front = _rear = 0;
		_size = size;
	}
	~Queue()
	{
		delete[] _pQue;
		_pQue = nullptr;
	}

	//为了避免浅拷贝,直接删除掉可能出现浅拷贝情况的复制重载和拷贝构造函数
	//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;

	}

	//入队操作,对尾入队
	void push (int val)
	{
		if (full())
		{
			resize();
		}
		_pQue[_rear] = val;
		_rear = (_rear + 1) % _size;
	}
	//出队操作
	void pop()
	{
		if (empty())
		{
			return ;
		}
		_front = (_front + 1) % _size;
	}
	//获取队头元素
	int top()	{return _pQue[_front];}
	bool full() {return (_rear + 1) % _size == _front; }
	bool empty() { return _front == _rear; }
private:
	//为了扩容 队列定义为指针
	int* _pQue;		//申请队列的数组空间
	int _front;		//指示队头的位置
	int _rear;		//指示队尾的位置
	int _size;		//队列扩容的总大小

	void resize()
	{
		int* ptmp = new int[2 * _size];
		int index = 0;
		for (int i = _front; i != _rear; i = (i + 1) % _size)
		{
			//从front遍历到rear 赋值给扩容后的队列
			ptmp[index++] = _pQue[i];
		}
		delete[] _pQue;
		_pQue = ptmp;
		_front = 0;
		_rear = index;
		_size *= 2;
	}
};

int main()
{
	Queue queue;
	for (int i = 0; i < 20; ++i)
	{
		queue.push(rand() % 100);
	}
	while (!queue.empty())
	{
		cout << queue.top() << endl;
		queue.pop();
	}

	return 0;
}

构造函数初始化列表

#include<iostream>
using namespace std;

class Test
{
public:
	Test(int data = 10)
		:mb(data)
		, ma(mb)
	{
		//构造函数列表初始化 顺序先初始化ma再初始化mb
		//成员变量的初始化和它们定义的顺序有关,和构造函数初始化列表中出现的先后顺序无关
		/*
			ma = mb(此刻mb并未初始化是一个不确定的值)
			mb = data (mb被初始化为10)
		*/
	}
private:
	int ma;
	int mb;
	//0XCCCC CCCC  -858993460
};





//日期类
class CDate
{
public:
	CDate(int y, int m, int d)
	{
		_year = y;
		_month = m;
		_day = d;
	}

	void show()
	{
		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)
		//构造函数的初始化列表:是按照定义的顺序初始化 第一个先初始化,如果用第二个初始化第一个数,就会发生无效值的问题
		:_data(y,m,d)		//制定了日期类型的构造方式
		,_amount(a)			//int _amount = a; 
		,_price(p)
	{
		//当前类类型构造函数体
		//不使用外部资源,不考虑浅拷贝带来的影响
		strcpy(_name, n);
		_amount = a;		//int _amount;_amount = a; 对于内置类型反汇编代码完全一致
		_price = p;
	}

	void show()
	{
		cout << "name:" << _name << endl;
		cout << "amount:" << _amount << endl;
		cout << "price" << _price << endl;
		_data.show();
	}
private:
	//属性,日期也是属性的一部分
	char _name[20];
	int _amount;
	double _price;
	CDate _data;	//成员对象 对象生成分为两步 1.分配内存 2.调用构造函数(没有指定默认调用默认构造函数,自定义构造函数后,编译器就不会产生默认构造函数)
};


int main()
{
	CGoods good("商品",100,35.0,2019,5,12);
	good.show();
	return 0;
}

类的各种成员方法和区别

#include<iostream>
using namespace std;

/*
	类的各种成员	- 成员方法/变量
	普通成员方法,	必须使用对象调用,对象的地址当作实参传给函数,
	成员方法多加一个this形参变量,用来接收调用他的this对象(通过this指针区分共享一套成员方法的this对象)
	//编译器会添加一个this形参变量
	1.属于类的作用域
	2.调用该方法时,需要依赖一个对象,没有对象无法调用普通成员方法(常对象无法调用 实参const CGoods*  形参CGoods*)
	3.可以任意访问对象的私有成员变量		(protect只有在继承的时候才会起作用)

	静态成员方法
	//不会生成this形参
	1.使用类名作用域来调用,而不是使用类对象来调用
	2.没有this指针,不依赖于对象,访问所有对象共享的信息
	3.属于类的作用域
	4.可以任意访问对象的私有成员,仅限于不依赖对象的成员(只能调用其他的static静态成员)

	常成员方法
	//const CGood*this
	1.属于类作用域
	2.调用依赖一个对象,普通对象和常对象皆可
	3.可以任意访问对象的私有成员,只能读,不能写,this指针被const修饰,所以不能改变



*/
class Test
{
public:
	Test(int data = 10)
		:mb(data)
		, ma(mb)
	{
		//构造函数列表初始化 顺序先初始化ma再初始化mb
		//成员变量的初始化和它们定义的顺序有关,和构造函数初始化列表中出现的先后顺序无关
		/*
			ma = mb(此刻mb并未初始化是一个不确定的值)
			mb = data (mb被初始化为10)
		*/
	}
private:
	int ma;
	int mb;
	//0XCCCC CCCC  -858993460
};





//日期类
class CDate
{
public:
	CDate(int y, int m, int d)
	{
		_year = y;
		_month = m;
		_day = d;
	}

	void show()
	{
		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)
		//构造函数的初始化列表:是按照定义的顺序初始化 第一个先初始化,如果用第二个初始化第一个数,就会发生无效值的问题
		:_data(y, m, d)		//制定了日期类型的构造方式
		, _amount(a)			//int _amount = a; 
		, _price(p)
	{
		//当前类类型构造函数体
		//不使用外部资源,不考虑浅拷贝带来的影响
		strcpy(_name, n);
		_amount = a;		//int _amount;_amount = a; 对于内置类型反汇编代码完全一致
		_price = p;
		//一个新对象的产生一定会调用构造函数,使用静态成员变量在构造函数中累加所有出现的对象
		_count++;			//记录所有产生的新对象的数量
	}

	void show()
	{
		cout << "name:" << _name << endl;
		cout << "amount:" << _amount << endl;
		cout << "price" << _price << endl;
		_data.show();
	}
	//常成员方法 和普通成员方法构成重载关系
	//只要是只读操作的成员方法,一律实现成const常成员方法
	void show() const	//const CGoods *this
	{
		//常成员方法只能调用 常方法 不能调用普通方法
		//普通方法和常方法的形参不同
		cout << "name:" << _name << endl;
		cout << "amount:" << _amount << endl;
		cout << "price" << _price << endl;
	}
	//普通成员方法
	//打印所有商品共享的信息
	void showCGoodsCount()
	{
		cout << "所有商品的种类数量是:" << _count << endl;
	}
	//静态成员方法
	static void showCGoodsCountA()
	{
		cout << "所有商品的种类数量是:" << _count << endl;
	}
private:
	//属性,日期也是属性的一部分
	char _name[20];
	int _amount;
	double _price;
	CDate _data;		//成员对象 对象生成分为两步 1.分配内存 2.调用构造函数(没有指定默认调用默认构造函数,自定义构造函数后,编译器就不会产生默认构造函数)
	static int _count;	//静态成员变量声明,时所有成员对象共享的  需要在类外进行定义,记录这个类所有对象商品的总数量
						//不属于对象,属于栈级别
};
//static 成员变量一定要在类外进行定义并且初始化 计算对象成员大小时,静态成员是不计入对象内存大小的 对象在stack段 静态成员变量在bss段
int CGoods::_count = 0;

int main()
{
	CGoods good("商品", 100, 35.0, 2019, 5, 12);
	good.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();

	//从语义上讲不符合,用good2打印了所有商品的信息
	good2.showCGoodsCount();
	//使用类名调用所有对象共享的信息,合乎语义规则
	CGoods::showCGoodsCountA();
	//统计所有商品的总数量

	const CGoods good5("非卖品", 10, 10.0, 10, 10, 10);
	//常对象调用普通方法出错
	// good5.show() == CGoods::show(&good5) 需求的形参CGoods*this  实际提供的实参const CGoods*

	return 0;
}

指向成员变量的指针

#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;
private:

};

int Test::mb = 10;
int main()
{
	Test t1;
	Test *t2 = new Test();
	//int *p = &Test::ma  无法从" int Test::* " // 转换为" int * "
	//加了类的作用域的指针 
	//指向的是类作用域里面的变量,不是全局的变量
	int Test::* p = &Test::ma;
	//给对象解引用才能使用指针访问,依赖对象
	t1.*p = 20;
	(t2)->*p = 30;
	(*t2).*p = 15;
	cout << t1.*p <<"  "<< endl;
	//	.*和->等价	t2是指针,存放地址  t1是对象

	//静态成员变量不依附于对象,不需要对象和作用域
	int* p1 = &Test::mb;
	*p1 = 40;
	cout << *p1 << endl;


	delete t2;
	return 0;
}

指向成员方法的指针

#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;
private:

};

int Test::mb = 10;
int main()
{
	Test t1;
	Test* t2 = new Test();

	//返回值是void,不带形参的一个函数
	void(Test::*pfunc)() = &Test::func;
	(t1.*pfunc)();
	(t2->*pfunc)();

	void(*func1)() = &Test::static_func;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值