128-C++学习第六弹

class Int
{
     
private:
	int value;
public:
	Int(int x=0):value(x){
     }
	~Int(){
     }
	int &Value(){
     return value;}
	const int&Value()const{
     return value;}
};

class Object
{
     
	int num;
	Int*pInt;
public:
	Object(int x=0):num(x),pInt(new Int(x+10)){
     }
	~Object()
	{
     
		if(pInt!=nullptr)
		{
     
			delete pInt;
		}
		pInt=nullptr;
	}
	int GetNum(){
      return num;}

	Int&GetInt(){
      return *pInt;}

	Int*GetPInt(){
     return pInt;}

};


int main()
{
     
	Object Obj(10);

	Int*p=Obj.GetPInt();

	Int x=Obj.GetInt();

	return 0;
}

当我们主函数运行的时候,给主函数开辟一个栈帧,定义了一个Obj对象,这个对象成员有:num和pInt, num=10,pInt 指针new一个Int的空间(4字节)x+10=20,拿20去初始化这个空间。new在这里2个动作:申请空间和拿Int的构造函数构建对象(初始化20),把构建好的这个对象的地址给pInt
Int*p=Obj.GetPInt(); 把pInt的内容给p,p指针指向20这个对象
Int x=Obj.GetInt(); 返回的是所指之物,把对象拷贝赋值给x
128-C++学习第六弹_第1张图片

Obj.GetPInt()->Value()=100;

按照函数的调用关系,从左向右依次调动,Obj.GetPInt()调动GetPInt()函数,返回Int类型的指针PInt,Value()是指向对象的方法,100给value值,实际上是给PInt指向的对象赋值,20变成100

问题:
如果GetInt()前面没有&

Int GetInt(){
      return *pInt;}

那么下面这三句可不可以执行?

Int x=Obj.GetInt();//可以
int a=Obj.GetInt().Value();//可以
Obj.GetInt().Value()=100;//没必要

当我们去调动GetInt()这个函数的时候,将会给这个函数开辟一个栈帧,把this指针给给它,把*pInt这个对象返回,要调动拷贝构造函数,以临时对象作为过渡(临时对象构建在主函数栈区),把这个临时对象赋值给x,所以第一行代码正确。第二行代码,前面返回的也是一个将亡值,把将亡值的value值取出来给a,第二行代码正确。但是第三行代码,是给将亡值的value值更改,改了之后无意义,这个值迅速就销毁了,将亡值是不能赋值的。

在GetInt()前面加上&,就OK了

Int&GetInt(){
      return *pInt;}

返回的就是pInt所指对象本身!100改变的是Obj里pInt所指之物的对象

在C++中,可以重载 * 重载->

Int& operator*() {
      return *pInt; }
const Int& operator*() const {
      return *pInt; }

Int* operator->() 
	{
      
		return pInt;  
		//return &**this; 两种写法都可以 
	}

Int& operator*() { return * pInt; }
重载 * ,返回的是pInt所指之物
const Int& operator*() const { return *pInt; }这个是常方法,返回的也是pInt所指之物,为了保持一致性,也已const Int& 形式返回,

int main()
{
     
	Object obj(10);
	Int a = *obj;
	int x = (*obj).Value();
	(*obj).Value() = 100;

	return 0;
}

现在相当于函数给了新的名字,重载了解引用
看程序:定义了一个对象obj,它的num=10,还有pInt指针指向Int的对象,构造函数构建Int类型的对象,值为20,*obj相当于调动自己的运算符重载,解引用,直接返回所指对象给a,(*obj).Value(),把20给x,(*obj).Value() = 100;把20变为100
这个重载 *和指针很像
128-C++学习第六弹_第2张图片

关于重载->

128-C++学习第六弹_第3张图片
this指向obj,this是对象本身, * * this调动指向重载,返回的是pInt指向对象的别名,&把这个对象的地址返回

int main()
{
     
	Object obj(10);
	obj->Value();
	obj.operator->()->Value();
	return 0;
}

obj.是对象本身的方法
obj->是重载了->,返回的对象是Int类型的对象,返回(*Int),->是先和对象本身结合,返回所指之物的类型,也就是(*Int),然后(*Int)->Value();实际上这个地方的 -> 作用了两次。只需要写一个->就可以
重载了 * 和->时,这个类型叫智能指针

重载()

仿函数

class Add_Int
{
     
private:
	int value;
public:
	Add_Int(int x = 0) :value(x) {
     }
	~Add_Int() {
     }
public:
	//int operator()(Add_Int * const this,int x,int y)
	inline int operator()(int x, int y)
	{
     
		value = x + y;
		return value;
	}
};
int main()
{
     
	Add_Int add;
	int x = add(10, 20);//重载()
	int y = add.operator()(10, 20);//解析
	//  y = operator()(&add,10,20);		//正确
	//  y = operator(&add)(10,20);		//错误,因为这里的()是函数名
	x = Add_Int(0)(12, 23);//类型名后面加()意味着构建对象,拿对象调动()重载

}

在标准库只有一元,二元仿函数

class Stack
{
     
private:
	double data[10000000];//超过内存,崩溃
	int maxsize;
	int pos;
public:
	Stack() {
      pos = -1; }
	~Stack() {
     }

	int size()const {
      return pos + 1; }
	int empty() const {
      return size() == 0; }
	int full() const {
      return size() == maxsize; }
	void push(double x)
	{
     
		pos += 1;
		data[pos] = x;
	}
	// pop
	double Top() {
      return data[pos]; }
	void  pop() {
      pos -= 1; }
};

int main()	  //主函数栈区大小 vs 1M // Liunx // 10M
{
     
	Stack st;//开辟在局部变量区
	return 0;
}

上面这个方法不好,类型不方便转换,不满足于用户

模板类型

template<typename Type>//Type是标识符
class Stack
{
     
	Type* data;
	int maxsize;
	int pos;
public:
	Stack(int sz = 10)
	{
     
		maxsize = sz;
		pos = -1;
		data = new Type[maxsize];  //???
		// 1 // 2;
		//cout << typeid(Type).name() << endl;
		//打印出int, double, student 
		//typeid(Type).name x;//错误 
		//     string
	}
	~Stack()
	{
     
		maxsize = 0;
		pos = -1;
		delete[]data;
		data = nullptr;
	}
	int size() const {
     
		return pos + 1;
	}
	bool empty() const {
     
		return size() == 0;
	}
	bool full() const {
     
		return size() == maxsize;
	}
	void push(const Type& x)
	{
     
		if (!full())
		{
     
			pos += 1;
			data[pos] = x;
		}
	}
	Type& top() {
     
		return data[pos];
	}
	const Type& top() const {
     
		return data[pos];
	}
	void pop() {
     
		pos -= 1;
	}
};

下面这三段为什么都加上const?
128-C++学习第六弹_第4张图片
我们可能定义普通的栈 Stack;
也可能定义常性的栈 const Stack;
普通对象可以调动普通方法,也可以调动常方法
常对象只能调动常方法
这样写加大此代码的复用程度

从整个类型设计的角度

128-C++学习第六弹_第5张图片
一个是常方法,值不能改
一个是普通方法,值可以修改

这个栈方法如何来使用的?

int main()
{
     
	Stack<int> ist(100);
	Stack<double> dst;
	Stack<Student> sst;
	///ist = dst;
	return 0;
}

编译器在编译时,发现你定义了一个模板类型的栈,把编译的模板类型的栈放在描述表里,在编译主函数的时候,发现给的int,生成一段代码。
128-C++学习第六弹_第6张图片
然后看到double,又生成了一段代码
128-C++学习第六弹_第7张图片
然后看到student,又生成一段代码
128-C++学习第六弹_第8张图片
模板类是生成类型的类型
128-C++学习第六弹_第9张图片
注意,这个过程是在编译时确定的
一定要在<>给具体的类型
128-C++学习第六弹_第10张图片
这三大段代码编译之后形成obj文件,链接形成可执行文件
模板是产生代码的代码(编译器进行)

只有在模板里面,调动这个方法时,才参与编译。没有调动此方法,此方法对不对编译器没办法检查是否有问题

栈的类型不同,不能互相赋值,类型不匹配,编译完成后,类型名发生改变,类型是Stack< int > 类型是Stack< double > 不一样的类型

接下来解决这个问题
128-C++学习第六弹_第11张图片

class Object
{
     
private:
	int value;
public:
	Object(int x = 0) :value(x)
	{
     
		cout << "construct object: " << this << endl;
	}
	~Object()
	{
     
		cout << "deconstruct object: " << this << endl;
	}
};

int main()
{
     
	Stack<Object> ost;
	cout << ost.empty() << endl;
	return 0;
}

定义一个空栈
128-C++学习第六弹_第12张图片
从逻辑上说,是一个空栈,空的为真

int main()
{
     
	Stack<Object> ost;
	cout << ost.size() << endl;
	return 0;
}

128-C++学习第六弹_第13张图片

入栈0,但是构建的时候,构建10个对象了,逻辑和物理上的差异

STL


#include
#include
#include
using namespace std;
int main()
{
     
	string s1("yhpinghello");
	string s2 = "yhping";

	string s3; 

	s3 = s1 + s2;
	s3 = s1 + "yhping";
	s3 = "yhping" + s1;
	s3 = "hello" + "yhping";//错误,这个不可行,常性字符串,没有+
	
	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;

	return 0;
}
int main()
{
     
	string s1("yhping");  
	string s2("yhx");

	cout << (s1 < s2) << endl;//比较
	s1.append("hello");//尾部接
	cout << s1 << endl;

	int len = s1.size();//大小
	int cap = s1.capacity();//容量一定大于字符串的个数
	for (int i = 0; i < len; ++i)
	{
     
		cout << s1[i] << " ";//下标访问
	}
	//s1[10] = 'x';//改变字符串内容 可以检查越界
	cout << s1 << endl;
	const char* s = s1.c_str();//返回s1字符串的地址
	return 0;
}

vector

相当于可扩展的数组

#include
#include
using namespace std;
int main()
{
     
	vector<int> iar = {
      12,23,34,45,56,67,78 };//可以直接初始化 
	vector<double> dar;
	//vector sar;要什么添加什么,vector就操作什么 

	int n = iar.size();//个数 
	for (int i = 0; i < n; ++i)
	{
     
		cout << iar[i] << " ";//打印 
	}
	iar.push_back(100);//尾部添加数据,但是没有头部添加的方法 
	
	int a = iar.back();//返回最后一个元素 
	int x = iar.front();//返回第一个元素 

	iar.clear();//清空
	return 0;
}

迭代器

//迭代器,面向对象版本的指针 
int main()
{
     
	vector<int> iar = {
      12,23,34,56,67,78 };

	vector<int>::iterator it = iar.begin();//内置类型迭代器,it迭代器类型,begin获取顺序元素的第一个元素的位置信息 
	for (; it != iar.end(); ++it)//end代表顺序元素的最后一个元素的后续位置,78后面的后续位置 
	{
     
		cout << *it << endl;//迭代器从前向后依次访问 ,*it所指之物 
	}

	return 0;
}

在这里插入图片描述

你可能感兴趣的:(C++学习,c++)