C++ 5:malloc和new,对象成员方法,实现整型栈,拷贝构造函数,运算符重载,数组名,缺省函数

1. malloc和new

1.1 初始化对象

初始化一个对象

int main()
{
	//new创建单个对象
	Complex* cp = new Complex(1, 2);
	cp->Print();
	delete cp;//释放一个对象
}

初始化一组对象

int main()
{
	//创建一组对象
	int n = 2;
	//cin >> n;
	Complex* cp = new Complex[n]{ {1,2},{2,3} };

	delete[] cp;//释放一组对象

	return 0;
}

在这里插入图片描述

1.2 申请对象

只有new关键字在申请空间时候才会申请4字节空间存放对象个数。
在这里插入图片描述

1.3 operator new

只是进行空间开辟,而不进行对象构建。

int main()
{
	int n = 10;
	Complex* cp = (Complex*)::operator new(sizeof(Complex) * n, nothrow);//不能抛出异常

	for (int i = 0; i < n; ++i)
	{
		new(&cp[i])Complex(i, i);//定位new,开辟空间完成后用定位new创建对象
	}
	return 0;
}

2. 对象成员方法的设计

2.1 const——常对象只能调动常方法

在这里插入图片描述

2.2 tmp对象能否改变?

在这里插入图片描述

2.3 全局对象能否设置成常方法?

不可以,因为全局对象是没有this指针。

2.4 成员方法能否以引用返回?

如果不受影响,就可以用引用返回。
在这里插入图片描述

2. 实现整型栈

2.1 初始化顺序

在这里插入图片描述

2.2 实现整型栈代码

#include<iostream>
#include<string>
using namespace std;

//实现整型栈
class MyStack
{
	enum { STACK_INIT_SIZE = 10, STACK_INC_SIZE = 2 };
	int* _data;
	int _size;
	int _top;
public:
	MyStack()
		:_data{ nullptr }, _size(STACK_INIT_SIZE), _top(-1)
	{
		_data = new int[_size];
	}
	~MyStack()
	{
		delete[] _data;
		_data = nullptr;
		_size = 0;
		_top = -1;
	}
	bool Resize(int size)//增容到size大小
	{
		if (size < Capacity()) return false;

		int* newdata = new(nothrow) int[size];
		if (newdata == nullptr)
		{
			return false;
		}
		memmove(newdata, _data, sizeof(int) * Size());
		delete[] _data;
		_data = newdata;
		_size = size;
		return true;
	}
	int Capacity() const//栈长度
	{
		return _size;
	}
	int Size() const//元素个数
	{
		return _top + 1;
	}
	bool Full() const//判满
	{
		return Size() == _size;
	}
	bool Empty()//判空
	{
		return Size() == 0;
	}
	bool Push(int val)//入栈
	{
		if (Full() && Resize(Capacity() * STACK_INC_SIZE))
		{
			return false;
		}
		_top += 1;
		_data[_top] = val;
		return true;
	}
	int Top() const//取栈顶元素,元素不出栈
	{
		return _data[_top];
	}
	bool Pop()//只是出栈
	{
		if (Empty())
		{
			return false;
		}
		_top -= 1;
		return true;
	}
	bool GetPop()//取栈顶元素和出栈
	{
		/*int tmp = _data[_top];
		_top -= 1;
		return tmp;*/
		return _data[_top--];
	}
};
int main()
{
	MyStack mys;
	mys.Resize(20000);//直接申请一块大内存,后面不用在进行增容,系统运行效率提高
	for (int i = 0; i < 100; ++i)
	{
		cout << mys.Capacity() << " " << mys.Size() << endl;
		mys.Push(i);
	}
	while (!mys.Empty())
	{
		cout << mys.Top() << endl;
		mys.Pop();
	}
}

运行结果
在这里插入图片描述

3. 拷贝构造函数

3.1 拷贝构造函数定义

用一个对象初始化另一个对象

同一个类的对象在内存中有完全相同的结构,如果作为一个整体进行复制或称拷贝是完全可行的。这个拷贝过程只需要拷贝数据成员,而函数成员是共用的(只有一份拷贝)。在建立对象时可用同一类的另一个对象来初始化该对象的存储空间,这时所用的构造函数称为拷贝构造函数(Copy Constructor)。

3.2 拷贝构造函数类型

  • ① 系统自动补充缺省构造函数
  • ② 系统自动补充缺省析构函数
  • ③ 系统自动补充拷贝构造函数

3.3 拷贝构造函数特点

  • 函数名和类型名相同,参数为类型的引用。
  • 调用构造函数创建对象,当用对象创建另一个对象,调用拷贝构造函数。
  • 析构几次就代表创建了几个对象。
    Object(const Object& obj)//加引用可以防止死递归,加const不允许对象进行修改
        :value(obj.value)
    {
        cout << "Copy Create" << endl;
    }

3.4 将亡值

当函数返回时候构建对象副本时产生的临时数值,将亡值对象是一个常性对象
在这里插入图片描述

3.5 拷贝构造函数返回引用与返回非引用的区别?

Object fun(const Object& obj)//3 用引用返回和不用引用返回区别:
{
    int val = obj.Value();
    Object obja(val);// 4
    return obja;// 调用拷贝构造函数构建将亡值 5
}

int main()
{
    Object objx(0);// 1
    Object objy(0);// 2
    objy = fun(objx);
    cout << objy.Value() << endl;
    return 0;
}

3.5 1 非引用返回:Object fun(const Object& obj)

进入主函数分配栈帧,为对象创建空间,调用构造函数创建对象,将将亡值对象的地址送入eax,将eax所指向的对象赋值给主函数变量(间接寻址)

3.5 2 引用返回:Object& fun(const Object& obj)(错误返回)

引用返回不用建立将亡值对象,只需要把地址存放给eax,eax存放变量地址

静态关键字只能修饰函数名,不能修饰其他

3.5.2.1 什么时候可以引用返回?

当对象的生存期不受函数影响时可以引用返回

Object& operator=(const Object& obj)
{
    this->value;
    return *this;
}

3.6 为什么要在主函数中构建将亡值?

主函数调用fun函数,所以将亡值构建在主函数的内存空间中。

3.7 析构顺序和构造顺序

代码示例

#include<iostream>
using namespace std;
class Object
{
    int value;
public:
    Object() { cout << "Object::Object" << this << endl; }//缺省构造函数
    Object(int x) :value(x) { cout << "Object::Object" << this << endl; }//带参构造函数
    ~Object(){ cout << "Object::~Object" << this << endl; }//系统补充析构函数
    void SetValue(int x) const { x = value; }
    int GetValue()const { return value; }

    Object(Object& obj)
        :value(obj.value)
    {
        cout << "Copy Create:" << this << endl;
    }
};
Object fun(Object obj)//3 如果要用形参改变实参,则不加const,如果不改变,则加const
{
    int val = obj.GetValue();
    Object obja(val);// 4
    return obja;// 调用拷贝构造函数构建将亡值 5
}
int main()
{
    Object objx(0);// 1
    Object objy(0);// 2
    objy = fun(objx);
    return 0;
}

运行结果
在这里插入图片描述

3.8 系统内存空间分配

在这里插入图片描述

4. 运算符重载

4.1 定义

函数名为合法标识符,操作符不能作为一个函数有效的函数名

4.2 代码示例

class Object
{
public:
    Object() {}//构造函数
    ~Object() {}//析构函数
    Object(const Object& x) {}//拷贝构造函数
    Object& operator=(const Object& x)//赋值重载语句
    {
    	if(this!=&obj)//防止相同类型进行赋值
    	{
    		this->value;
    	}
        return *this;
    }
    Object* operator&()//普通对象取地址符重载
    {
        return this;
    }
    const Object* operator&()const//常对象取地址符重载
    {
        return this;
    }
    Object(Object&& x) {}//移动构造函数
    Object& operator=(const Object&& x) {}//移动赋值函数
};

4.3 常用的运算符重载函数

常用函数
1构造函数
2拷贝构造函数
3析构函数
4赋值重载语句
5普通对象取地址符重载
6常对象取地址符重载
7移动构造函数
8移动赋值函数
    c3 = c1 + c2;
    //c3 = c1.operator+(c2);
    //c3 = operator+(&c1, c2);

4.4 如何防止函数自赋值?

Object& operator=(const Object& obj)
{
    if (this != &obj)
    {
        this->value = obj.value;
    }
    return *this;
}

5. 数组名

【类型(类型推演)和大小(替换)】

int main()
{
    int ar[10] = { 12,23,34,45,56,67,78,89,90,100 };

    sizeof(ar);//代表整个数组
    int(*p)[10] = &ar;//整个数组地址
    int(&br)[10] = ar;//引用数组
}

6. 缺省函数

6.1 运算符重载函数特点

  • (1)运算符重载函数的函数名必须为关键字operator加一个合法的运算符。在调用该函数时,将右操作数作为函数的实参。
  • (2)当用类的成员函数实现运算符的重载时,运算符重载函数的参数(当为双目运算符时)为一个或(当为单目运算符时)没有。运算符的左操作数一定是对象,因为重载的运算符是该对象的成员函数,而右操作数是该函数的参数。
  • (3)单目运算符“++”和“–”存在前置与后置问题。

6.2 前置++与后置++

前置++返回的是对象本身,后置++返回局部对象

Int& operator++()
{
    this->value;
    return *this;
}//++a;返回对象本身
Int


 operator++(int)
{
    Int old = *this;
    ++* this;
    return old;
}//c=a++;返回局部对象

6.2.1 前置“++”格式

返回类型 类名::operator++(){……}

6.2.2 后置“++”格式

返回类型 类名::operator++(int){……}

后置“++”中的参数 int 仅用作区分,并无实际意义,可以给一个变量名,也可以不给变量名。

6.2.3 前置++为单目,后置++为双目

代码示例

Int& operator++()
{
    value += 1;
    return *this;
}
Int operator++(int)
{
    Int tmp = *this;
    ++* this;
    return tmp;
}

代码示例

Int& operator+(const Int& it) const//对象和对象相加
{
    return Int(this->value + it, value);
}
Int& operator+(const int x) const//对象和整型变量相加
{
    return Int(this->value + x);
}
Int operator+(const int x, const Int& it)//整型变量和对象相加
{
    return it + x;
}
bool operator==(const Int& it) const//判断两个对象的值是否相等
{
    return this->value == it.value;
}
bool operator! = (const Int & it) const//判断两个对象的值是否不相等
{
    return(*this == it);
}
bool operator<(const Int& it) const//小于
{
    return this->value < it.value;
}
bool operator>=(const int& it) const//大于等于
{
    return!(this < it);
}
bool operator>(const int& it) const//大于
{
    return this->value > it.value;
}
bool operator<=(const int& it) const//小于等于
{
    return!(this > it);
}

重载自加和加号运算符时,以函数方式进行

6.2.4 前置++和后置++在编译器的内部运行过程

Int operator++(int)
{
    Int old = *this;
    ++* this;
    return old;
}
Int operator++(const int x) const
{
    return Int(this->value + x);
}

int a = 0;
a = a++ + 1;
//编译器进行过程
a = a.operator++(0) + 1;
a = a.operator++(&a, 0) + 1;//第一个参数a传递给this指针,第二个参数用于区分前置和后置,只要是整型数值均可
a = operator++(&a,0).operator+(1)
a = operator+(&operator++(&a,0),1);//用对象调动成员方法,将对象地址传入相加

//赋值语句重载
a.operator=(operator+(&operator++(&a, 0), 1));
operator=(&a, operator+(&operator++(&a, 0), 1));
//1.对a进行+1操作  2.a的值变为1  3.后置++返回对象  4.返回对象与1相加变为1  5.相加后的值返回给a=1

6.2.5 普通对象前置++和后置++

//举例:中缀表达式变为后缀表达式之后可以去掉括号等多余东西
a + b * c;
//1. a  b  c  *  +
//2. a  b*c  +入栈顺序

6.2.6 在进行运算符重载时候什么情况下以引用返回,什么时候以值返回?

两个整数相加有返回值,赋值语句相加也有返回值。若运算符操作后返回自身,以引用返回;如果返回临时量或者将亡值,就返回值。

6.3 C++中不允许重载的运算符(极少数)

在这里插入图片描述
# , ## , // , /**/ 不允许重载

仿函数/重载()运算符

int main()
{
	Add add;
	int x = 10, y = 20;
	int c = 0;
	c = add(x, y);//对象函数/仿函数(对象调用)
	c = add.operator()(x, y);
	c = Add()(12, 23);//类+()+()=对象+()调动成员方法
	return 0;
}

6.4 重载运算符的限制

  • 不可臆造新的运算符
  • 不能改变运算符原有的优先级、结合性和语法结构,不能改变运算符操作数的个数
  • 运算符重载不宜使用过多
  • 重载运算符含义必须清楚,不能有二义性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值