new/delete 和malloc/free 的区别
共同点:
功能相同,new 和malloc 都是在堆区申请指定大小的内存空间,delete 和 free 都是释放指定的一块堆区内存空间(内部实现细节不同,不要混用)。
不同点:
new 和 delete 是 C++中新增的运算符,而malloc 和 free 是函数,所以运算符的执行效率比函数高(对于非对象数据)。
对于非对象数据,它们基本没有区别。
对于对象数据:
- 使用 new 运算符在堆区给对象申请空间后,还会调用对像的某个匹配的构造函数,而malloc 函数仅仅就是给对象申请堆区空间,并不会调用其构造函数。
- 使用 delete 运算符释放对象时,它首先会调用一次该对象的析构函数,然后再释放对象所占的堆区空间,而 free 函数是直接释放对象所占的堆区空间,并不会调用对象的析构函数。
运算符重载(Operator Overload)
重新定义运算符的功能(面向对象)。
运算符重载函数也是类中的特殊方法,其方法必须"operator 运算符"这种形式。
运算符重载函数既可以为成员函数,也可以为全局函数,建议尽量使用成员函数。
每个类中都有一个默认的赋值运算符重载函数,它实现的是浅拷贝效果,如果不能满足需求,我们可以自己重载它,
五个不可重载的运算符:
- .(成员运算符),通常用于访问对象的成员
- ::(作用域运算符)
- .*(成员指针运算符),即成员是指针类型,访问指针类型成员指向的数据。
- ?: (条件运算符)
- sizeof(求长度运算符)
仿函数(Functor):重载小括号运算符,所有对象可以像函数调用一样的形式使用。
设计思想:高类聚、低耦合
- 高类聚是指一个模块内部各个元素之间关系紧密,争取用最少的元素和方法实现相应的功能。
- 低耦合是指一个程序中各个模块之间的联系少和相互依赖程度低
友元(Friend)
在一个类的友元中可以访问该类的所有成员,如同本类中一样。
友元分为两种:
- 友元函数
- 友元类
临时无名对象
生命周期很短,昙花一现,定义它的语句执行完毕就被释放了。
对象类型转换问题
explicit 关键字:用于修饰构造函数(主要是那些带一个参数的构造函数),表示必须显式调用该构造函数,不会被隐式调用;
带一个参数的构造函数可以用于类型转换,即将其他类型数据转型为对象。
通过重载类型转换运算符(类型转换函数)可以实现将对象转型为其他类型。
隐式调用:类名 p =5; //此时这个5作为一个临时无名的对象,被创建赋给p,然后立马被释放。
显式调用:利用 explicit 关键字修饰改造函数,在定义对象时,必须严格按照构造函数参数列表来写
- 类名 p {5};
- 类名 p (5);
注:一般情况下,出现在带一个参数的构造函数中,使编译器选择为难。
实例一:malloc/free-new/delete
#include <iostream>
#include <cstring>
using namespace std;
class Person
{
private:
int p_sno = 0;
char* p_name = nullptr;
public:
Person()
{
p_name = new char[20];
p_name[0]= '\0';
}
Person(int sno,const char* name):p_sno(sno)
{
name = new char[20];
strcpy(p_name,name);
}
void show(void) const
{
cout << p_sno << " " << p_name << endl;
}
//拷贝构造函数
Person(const Person &p) //引用
{
// 浅拷贝:直接将对象属性的值赋给新对象的属性
// memcpy(&p_sno,&(p.p_sno),sizeof(p_sno));
// memcpy(&p_name,&(p.p_name),sizeof(p_sno));
// memcpy(this, &p, sizeof(p));
// 深拷贝:如果属性为指针,则将指针指向空间的内容赋给新对象指针空间的内容
p_name = new char[20];
// p_sno = p.p_sno;
memcpy(&p_sno,&(p.p_sno),sizeof(p_sno));
memcpy(p_name,p.p_name,20);
}
//重载 赋值 = 运算符
Person& operator=(const Person& p)
{
memcpy(p_name,p.p_name,20);
p_sno = p.p_sno;
return *this;
}
~Person()
{
cout << p_name << "快死了" << endl;
// delete 内部实现:
// 先调用析构函数,等析构函数返回后,才会释放。
// delete this;此时会无限递归,导致栈溢出。
delete[] p_name; //p_name为数组
}
};
void show1(Person p); //传对象
void show2(Person& p); //传对象引用
Person create();
int main()
{
// molloc与free属于函数,new/delete 属于运算符
// Person* p1 = (Person*)malloc(sizeof(Person));
// p1->show();
// free(p1);
//Person p(1001,"zz");
// //show1(p);
// show2(p);
// Person p = create();
// cout << &p << endl;
Person p1;
//Person p2 = p1; //创建P2用拷贝构造函数等效于:Person p2 {p1}
Person p2;
p1 = p2; //调用无参构造函数
// cout << &p1 << endl;
// cout << &p2 << endl;
return 0;
}
void show1(Person p)
{
// 内部实现:Person p = p1;相当于创建了一个布局的对象,浪费资源,时间
p.show();
}
void show2(Person& p)
{
p.show();
}
Person create()
{
Person p(1002,"李四");
cout << &p << endl;
return p;
}
实例二:友元
#include <iostream>
using namespace std;
class B;
class A
{
private:
int a;
void show_a()
{
cout << a << endl;
B* b;
}
// 将 B 类声明为当前类的友元类
friend class B;
};
class B
{
public:
void show_b()
{
A a;
a.a = 3;
a.show_a();
}
};
int main()
{
B b;
b.show_b();
return 0;
}
实例三:重载操作符
#include <iostream>
#include <cstring>
using namespace std;
class Student
{
private:
int sno;
string name;
public:
Student():sno(0),name("")
{
}
Student(int sno,string name):sno(sno), name(name)
{
}
explicit Student(int sno):sno(sno), name("匿名")
{
cout << "Student(int)" << endl;
}
~Student()
{
cout << sno << ' ' << name << " ~Student()" << endl;
}
void show() const
{
cout << sno << ' ' << name << endl;
}
int getSno() const
{
return sno;
}
//运算符重载(Operator Overload)
// 重载比较(>)运算符 //左操作数只能为类、结构体、共用体
bool operator >(int i); //> 属于双目运算符,右操作数为传入的参数
double operator >(const Student &s){
return 3.14;
}
bool operator<(const Student& s)
{
return sno < s.sno;
}
bool operator>=(int i)
{
return sno >= i;
}
// 单目运算符
// 前后置运算符对于非对象的操作效率一样
// 对于对象,前置运算符效率高一些,后置低一些。
// 重载前置++p,p为对象;
Student& operator++()
{
sno++;
return *this;
}
// 重载后置p++
Student operator++(int) //参数写一个int,编译器认为是后置,此参数为 “哑元” 参数
{
Student tmp = *this;
sno++;
return tmp;
}
// 重载下标运算符
char operator[](int i)
{
return name[i];
}
// 重载下标运算符:实现名字判断
string operator[](const char* i)
{
if(strcmp(i,"name") == 0) return name;
else return "error";
}
// 重载(强制)类型转换运算符
operator int()
{
return sno;
}
// 重载加法运算符
Student operator+(const Student& s)
{
Student tmp;
tmp.sno = sno + s.sno;
tmp.name = name + s.name;
return tmp;
}
// 重载括号运算符
int operator()(int i, int j)
{
return 5;
}
// 声明友元函数
friend istream& operator>>(istream& in,Student &s);
friend ostream& operator<<(ostream& out, const Student& s);
};
// 重载 比较运算符的定义
bool Student::operator >(int i) //> 属于双目运算符,左操作数为对象,右操作数为传入的
{
return sno > i;
}
// 重载
bool operator>=(int i,const Student& s) //全局
{
return i >= s.getSno();
}
//使用运算符时没有指定,先在类中找符合项,没有找到再去全局区
istream& operator>>(istream& in, Student& s)
{
cout << "学号:";
// int sno;
// in >> sno;
// //法一:setSno()传递,不太靠谱
in >> s.sno; //法二:友元函数
cout << "姓名:";
in >> s.name;
return in;
}
ostream& operator<<(ostream& out, const Student& s)
{
out << s.sno << ' ' << s.name <<endl;
return out;
}
int main()
{
// // (++p1).show();
// // (p1++).show();
// // p1.show();
// // 100 >= p2; // operator>=(100, p2);
// // p2 >= 100; // p2.operator>=(100); operator>=(p2, 100);
// // // p1 = p2;
// // double d = (p1 > p2); // double d = (p1.operator>(p2));
// // cout << d << endl;
// // if(p1 < p2) // if(p1.operator<(p2))
// // {
// // cout << "yes" << endl;
// // }
// // p1 > 1000; // p1.operator(1000);
// Student p1,p2;
// p1 = p2;
// if(p1 > p2) //if(p1.operator<(p2))
// {
// cout << "yes" << endl;
// }
// p1 + p2;
// p1 - p2;
// p1 > p2;
// p1 && p2;
// p1[3];
// int i = int(p1);
Student s2(3); // Student s2(3);
s2 = Student(5);
// 仿函数(Functor)
cout << s2(10, 9) << endl; // 括号运算符
// 临时无名对象,生命周期很短
// Student(1001, "李四").show();
Student p1(1001,"zhangsan"),p2 {1002,"lisi"};
cout << p1 << endl; //先判断 << 是否有重载方法,再判断 p1是否有重载方法(类型转换)
// cout << (int)(p1) <<endl; //
// cout << int(p2) << endl;
// cout << p2["name"] << endl;
// cout << p1[2] <<endl; //指定位输出
return 0;
}