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
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所指之物的对象
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
这个重载 *和指针很像
关于重载->
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?
我们可能定义普通的栈 Stack;
也可能定义常性的栈 const Stack;
普通对象可以调动普通方法,也可以调动常方法
常对象只能调动常方法
这样写加大此代码的复用程度
从整个类型设计的角度
这个栈方法如何来使用的?
int main()
{
Stack<int> ist(100);
Stack<double> dst;
Stack<Student> sst;
///ist = dst;
return 0;
}
编译器在编译时,发现你定义了一个模板类型的栈,把编译的模板类型的栈放在描述表里,在编译主函数的时候,发现给的int,生成一段代码。
然后看到double,又生成了一段代码
然后看到student,又生成一段代码
模板类是生成类型的类型
注意,这个过程是在编译时确定的
一定要在<>给具体的类型
这三大段代码编译之后形成obj文件,链接形成可执行文件
模板是产生代码的代码(编译器进行)
只有在模板里面,调动这个方法时,才参与编译。没有调动此方法,此方法对不对编译器没办法检查是否有问题
栈的类型不同,不能互相赋值,类型不匹配,编译完成后,类型名发生改变,类型是Stack< int > 类型是Stack< double > 不一样的类型
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;
}
int main()
{
Stack<Object> ost;
cout << ost.size() << endl;
return 0;
}
入栈0,但是构建的时候,构建10个对象了,逻辑和物理上的差异
#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;
}
相当于可扩展的数组
#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;
}