c++面向对象基础编程——运算符重载(二)

简介: c++面向对象基础编程——运算符重载(二)

赋值运算符重载


浅拷贝与深拷贝


如果一个类的数据成员中有指向动态分配空间的指针,那么通常情况下应该定义拷贝构造函数,并重载赋值运算符,否则会出现运行错误。

下面我们来看一下一段代进行分析:


#include<iostream>
using namespace std;
class Namelist
{
  char* name;
public:
  Namelist(int size)
  {
  name = new char[size];
  }
  //析构函数;
  ~Namelist()
  {
  delete[]name;
  }
};
int main()
{
  Namelist n1(10), n2(10);
  n2 = n1;
  return 0;
}


这个时候运行程序,你会发现程序无法运行。

那么为什么会出现这种情况?

首先构造函数以new动态分配存储块给对象n1和n2,然后把右边对象n1的值赋给n2,从而实现了类对象的赋值。如下图:


4e175d18406ad3284962e1c9c2a1bf53_862e960c2c8a48938c05b9780fc1697d.png


但是在最后,n2被析构函数清除后,此时堆区的模块就被收回堆区,但是在n1被析构函数删除的时候,,析构函数会继续寻找模块,所以会冲突。导致报错。这个时候,我们就可以通过重载默认赋值函数和拷贝构造函数解决。


重载赋值运算符的格式


Classname&Classname::operator=(Clasname obj)


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cstring>
using namespace std;
class Namelist
{
  char* name;
public:
  Namelist(char *p)
  {
  name = new char[strlen(p) + 1];
  if (name != 0)
  strcpy(name, p);
  }
  //析构函数;
  ~Namelist()
  {
  delete [] name;
  }
  Namelist& operator=(char* p);
  Namelist& operator=(Namelist&);
  void display() { cout << name << endl; }
};
//重载赋值运算符,完成常量给对象赋值;
Namelist& Namelist::operator=(char* p)
{
  name = new char[strlen(p) + 1];
  if (name != 0)
  strcpy(name, p);
  return *this;
}
//重载赋值运算符,完成类对象之间的赋值;
Namelist& Namelist::operator=(Namelist& a) //设置为一个参数的原因和前面=的重载原因一样,有this指针;
{
  if (this != &a)
  {
  delete name;  //删除原有对象的类容;
  name = new char[strlen(a.name) + 1];
  strcpy(name, a.name);
  }
  return*this;
}
int main()
{
  Namelist n1("I like you"), n2("bei jing li gong da xue");
  cout << "赋值前的数据:" << endl;
  n1.display();
  n2.display();
  cout << "赋值后的数据:" << endl;
  n1 = n2;
  n1.display();
  n2.display();
  return 0;
}


c871cb18fe7b6738f388c8de9cfd06d3_045bd6e47336459493b89ec32fafbcc1.png



重载赋值运算符函数的返回值


重载赋值运算符时,通常是返回调用该运算符对象的引用,不过不能使用引用返回一个局部对象,单this可以解决这个问题。只要非静态成员函数在运行,那么this指针就在作用内。

就像上面的代码一样,只需要一个形参既可以,这也就是this指针的应用。


赋值运算符重载函数和拷贝构造函数的区别


拷贝构造函数和运算符重载函数都是用来拷贝一个类的对象给另一个同类型的对象。要注意拷贝构造函数于赋值重载运算符的使用区别。

(1).拷贝构造函数是用已存在对象的各成员的当前值来创建一个新的对象,在下面的三种情况下,系统会自动调用拷贝构造函数。


1.当说明新的类对象的同时,要给它赋值另一个当前已经存在的对象的当前值

2.当对象作为函数的赋值参数而对函数进行调用要进行实参和形参的结合时

3.当函数的返回值是类的对象,载函数调用结束后返回到主调函数处时。

(2).赋值运算符重载函数要把一个已存在对象的各成员当前赋值给另一个已存在的同类对象。

eg:


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cstring>
using namespace std;
class Namelist
{
   char *name;
public:
  Namelist(char* p)
  {
  name = new char[strlen(p) + 1];
  if (name != 0)      //在c++中,0也就表示空;
  {
    strcpy(name, p);
  }
  }
  Namelist() { };
  Namelist(Namelist&);
  Namelist& operator=(const char* p);
  Namelist& operator=(Namelist&);
  void display() {
  if(name)
  cout << name << endl;
  }
  ~Namelist()
  {
  delete []name;
  }
};
Namelist::Namelist(Namelist& a)               //定义拷贝构造函数;
{
  name = new char[strlen(a.name) + 1];
  if (name != NULL)
  {
  strcpy(name, a.name);
  }
}
//第一个赋值运算符重载函数,完成常量的赋值;
Namelist& Namelist::operator=(const char* p)
{
  name = new char[strlen(p) + 1];
  if (name != NULL)
  {
  strcpy(name, p);
  }
  return *this;
}
//第二个赋值运算符重载函数,完成对象之间的赋值;
Namelist& Namelist::operator=(Namelist& p)
{
  if (this != &p)
  {
  delete[]name;
  name = new char[strlen(p.name) + 1];
  if(name!=NULL)
  strcpy(name, p.name);
  }
  return *this;
}
int main()
{
  Namelist n1("first object"), n2("second object"), n3;
  cout << "赋值前的数据:" << endl;
  n1.display();
  n2.display();
  n3 = "third obkect";   //调用第一个赋值运算符重载函数;
  n2 = n1;         //调用第二个赋值运算符重载函数;
  Namelist n4(n2);     //调用拷贝构造函数;
  cout << "赋值后的函数值:" << endl;
  n1.display();
  n2.display();
  n3.display();
  n4.display();
  return 0;
}



特殊运算符重载


"[ ]"运算符重载

对下标运算符“[ ]”重载的时候只能重载为成员函数,不可重载为友元函数。若在某自定义类中重载了下标运算符,则可将该类的类对象当作一个“数组”。从而对该类对象通过使用下标的方式来访问其中成员的数据,各下标变量的具体取值于类对象数据成员间 的对应关系完全由程序员在重载函数中设计和规定。

例如,重载下标运算符,访问数组原素,进行越界检查


#include<iostream>
#include<process.h>  //函数调度头文件,在使用进程终止函数的时候使用。
using namespace std;
const int LIMT = 100;     //定义一个常量
class Intarray
{
private:
  int size;     //数组大小;
  int* array;  //数组名;
public:
  Intarray(int = 1);    //默认为一个元素数组;
  int& operator[](int n);
  ~Intarray();
};
Intarray::Intarray(int i)
{
  //数组越界检查;
  if (i<0||i>LIMT)
  {
  cout << "out of array limit" << endl;
  exit(1);
  }
  size = i;
  array = new int[size];
}
int& Intarray::operator[](int n)
{
  //下标越界检查;
  if (n < 0 || n >= size)
  {
  cout << "out of range" << endl;
  exit(1);
  }
  return array[n];
}
Intarray::~Intarray()
{
  delete[]array;
  size = 0;
}
int main()
{
  int k, num;
  cout << "please input size of array(1~100):";
  cin >> k;
  Intarray array(k);
  for (int j = 0; j < k; j++)
  {
  array[j] = j * 10;
  }
  cout << "please input number of output array(1~100):";
  cin >> num;
  for (int j = 0; j < num; j++)
  {
  int temp = array[j];
  cout << "Element" << "array[" << j << "]" << "is" << temp << endl;
  }
  return 0;
}


78e63d9c2920e040bb3131ade8a2abf9_fb022b27388a417393be534a267fe876.png


“()”运算符的重载

他的格式基本和前面学的运算符重载的格式相同;就不过多介绍了。他的重载方式和“【】”运算重载的方式相同,只能通过成员函数的方式重载。


#include<iostream>
using namespace std;
class Func
{
private:
  double X, Y, Z;
public:
  double GetX() { return X; }
  double GetY() { return Y; }
  double GetZ() { return Z; }
  double operator() (double x, double y, double z);
};
double Func::operator()(double x, double y, double z)
{
  X = x;
  Y = y;
  Z = z;
  return 5 * x + 6 * y - 7 * z + 8;
}
int main()
{
  Func f;
  f(3.2, 4.5, 5.6);   //f.operator()_(3.2,4.5,5.6)
  cout << "func(";
  cout << f.GetX() << "," << f.GetY() << "," << f.GetZ() << ")=";
  cout << f(3.2, 4.5, 5.6) << endl;
  return 0;
}



下面我们重载函数访问二维数组元素,bing进行越界检查;

eg:


#include<iostream>
#include<process.h>
using namespace std;
const int LIMIT = 100;
class Intarray
{
private:
  int size1;       //行数;
  int size2;       //列数;
  int* array;      //数组名;
public:
  Intarray(int = 1, int = 1); //默认数组1行1列;
  int& operator() (int i, int j);
  ~Intarray();
};
Intarray::Intarray(int i, int j)
{
  //数组越界检查;
  if ((i<0 || i>LIMIT) || (j<0 || j>LIMIT))
  {
  cout << "out of array limit" << endl;
  exit(1);
  }
  size1 = i;
  size2 = j;
  array = new int[size1 * size2];
}
int& Intarray::operator()(int m, int n)
{
  //下标越界检查;
  if ((m < 0 || m >= size1) || (n < 0 || n >= size2))
  {
  cout << "out of range" << endl;
  exit(1);
  }
  return array[m * size1 + n];
}
Intarray::~Intarray()
{
  delete[]array;
  size1 = 0;
  size2 = 0;
}
int main()
{
  int r, c, m, n, i, j;
  cout << "please input row&&col of array(1~100):";
  cin >> r >> c;
  Intarray array(r, c);
  for (i = 0; i < r; i++)
  {
  for (j = 0; j < c; j++)
  {
    array(i, j) = 2 * i + j;
  }
  }
  cout << "please input row&&col numbers of output array(1~100):";
  cin >> m >> n;
  for (i = 0; i < m; i++)
  {
  for (j = 0; j < n; j ++ )
  {
    int temp = array(i, j);
    cout << "Element"; 
    cout << "array[" << i << "," << j << "]" << "is " << temp << endl;
  }
  return 0;
  }
}



类类型转换运算符重载


在c++中,类是用户自定义的类型,和c语言中的结构体类似。类与类之间,类与基本数据类型之间都可以实现数据类型的转换。实现这种转换需要使用构造函数和类类型转换运算符。


基本类型到类类型的转换

利用构造函数实现从基本类型到类类型的转换。前提是类中一定要具有只有一个非默认参数的构造函数。

如上面的程序就是利用了构造函数实现类型的转换。


类类型到基本类型的转换

格式:


operator<返回类型名>()
{
return <基本数值类型>;
}
#include<iostream>
using namespace std;
class Type
{
public:
  Type(int a, int b = 1);           //只有一个非默认参数的构造函数;
  operator double();
private:
  int data1, data2;
};
Type::Type(int a, int b)
{
  data1 = a;
  data2 = b;
}
Type::operator double()
{
  return double(data1) / double(data2);
}
int main()
{
  Type c1(2, 4), c2(3, 8);
  cout << "c1=" << c1 << "c2=" << c2 << endl;
  return 0;
}


ddcc74071b6b188f69c33e33656562bd_8c55a8c395bd4f3e950f69fc62b5f4b4.png


总结和应用


#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<process.h>
using namespace std;
class Vector
{
private:
  int size;       //数组大小
  int* array;//数组名;
public:
  Vector(int = 1);       //构造函数,默认为一个元素数组;
  Vector(Vector& a);        //拷贝构造函数;
  int& operator[](int n);
  Vector& operator+(Vector c); //成员函数形式重载;
  Vector& operator-(Vector c);   //重载-;
  Vector& operator=(Vector& a);
  int operator()();
  ~Vector();
};
Vector::Vector(int i)
{
  size = i;
  array = new int[size];
  for (int i = 0; i < size; i++)
  {
  array[i] = 0;
  }
}
int& Vector::operator[](int n)
{
  if (n < 0 || n >= size)
  {
  cout << "erro" << endl;
  exit(1);
  }
  return array[n];
}
Vector temp(4);
Vector& Vector::operator+(Vector c)
{
  for (int i = 0; i < 4; i++)
  {
  temp[i] = 0;
  }
  for (int i = 0; i < 4; i++)
  {
  temp[i] = array[i] + c.array[i];
  }
  return temp;
}
Vector& Vector::operator-(Vector c)
{
  for (int i = 0; i < 4; i++)
  {
  temp[i] = 0;
  }
  for (int i = 0; i < 4; i++)
  {
  temp.array[i] = array[i] - c.array[i];
  }
  return temp;
}
Vector::Vector(Vector& a)
{
  array = new int[size];
  if (array != 0)
  {
  for (int i = 0; i < 4; i++)
  {
    array[i] = a.array[i];
  }
  }
}
Vector& Vector::operator=(Vector& a)
{
  if (this != &a)
  {
  delete[]array;
  array = new int[size];
  if (array != 0)
  {
    for (int i = 0; i < 4; i++)
    {
    array[i] = a.array[i];
    }
  }
  }
  return *this;
}
int Vector::operator()()
{
  return size;
}
Vector::~Vector()
{
  delete[]array;
  size = 0;
}
int main()
{
  int j, length;
  Vector X(4), Y(4), Sum(4), Sub(4);
  for (j = 0; j < 4; j++)
  {
  X[j] = j + 2;
  Y[j] = j * 2;
  }
  cout << "first vector=(";
  for (j = 0; j < 4; j++)
  {
  int temp = X[j];
  if (j > 0)
  {
    cout << ",";
  }
  cout << temp;
  }
}
cout << ")" << endl;
cout << "second vector=(";
for (j = 0; j < 4; j++)
{
  int temp = Y[j];
  if (j > 0)
  {
  cout << ",";
  }
  cout << temp;
}
cout << ")" << endl;
Sum = X + Y;
Sub = X - y;
cout << "sum=(";
for (j = 0; j < 4; j++)
{
  int temp = Sum[j];
  if (j > 0)
  cout << ",";
  cout << temp;
}
cout << ")";
cout << "Sub=(";
for (j = 0; j < 4; j++)
{
  int temp = Sub[j];
  if (j > 0)
  {
  cout << ",";
  }
  cout << temp;
}
cout << ")" << endl;
length = X();
cout << "length of Vector is" << length << endl;
return 0;


相关文章
|
7天前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
44 18
|
7天前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
32 13
|
7天前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
26 5
|
7天前
|
存储 C++
【C++面向对象——输入输出流】处理二进制文件(头歌实践教学平台习题)【合集】
本任务要求使用C++读取二进制文件并在每行前添加行号后输出到控制台。主要内容包括: 1. **任务描述**:用二进制方式打开指定文件,为每一行添加行号并输出。 2. **相关知识**: - 流类库中常用的类及其成员函数(如`iostream`、`fstream`等)。 - 标准输入输出及格式控制(如`cin`、`cout`和`iomanip`中的格式化函数)。 - 文件的应用方法(文本文件和二进制文件的读写操作)。 3. **编程要求**:编写程序,通过命令行参数传递文件名,使用`getline`读取数据并用`cout`输出带行号的内容。 4. **实验步骤**:参考实验指
24 5
|
7天前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
20 5
|
7天前
|
Serverless 编译器 C++
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
24 4
|
7天前
|
设计模式 IDE 编译器
【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】
本项目旨在通过C++编程实现一个教学游戏,帮助小朋友认识动物。程序设计了一个动物园场景,包含Dog、Bird和Frog三种动物。每个动物都有move和shout行为,用于展示其特征。游戏随机挑选10个动物,前5个供学习,后5个用于测试。使用虚函数和多态实现不同动物的行为,确保代码灵活扩展。此外,通过typeid获取对象类型,并利用strstr辅助判断类型。相关头文件如&lt;string&gt;、&lt;cstdlib&gt;等确保程序正常运行。最终,根据小朋友的回答计算得分,提供互动学习体验。 - **任务描述**:编写教学游戏,随机挑选10个动物进行展示与测试。 - **类设计**:基类
23 3
|
3月前
|
存储 C++ UED
【实战指南】4步实现C++插件化编程,轻松实现功能定制与扩展
本文介绍了如何通过四步实现C++插件化编程,实现功能定制与扩展。主要内容包括引言、概述、需求分析、设计方案、详细设计、验证和总结。通过动态加载功能模块,实现软件的高度灵活性和可扩展性,支持快速定制和市场变化响应。具体步骤涉及配置文件构建、模块编译、动态库入口实现和主程序加载。验证部分展示了模块加载成功的日志和配置信息。总结中强调了插件化编程的优势及其在多个方面的应用。
487 68
|
2月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
123 5
|
3月前
|
安全 程序员 编译器
【实战经验】17个C++编程常见错误及其解决方案
想必不少程序员都有类似的经历:辛苦敲完项目代码,内心满是对作品品质的自信,然而当静态扫描工具登场时,却揭示出诸多隐藏的警告问题。为了让自己的编程之路更加顺畅,也为了持续精进技艺,我想借此机会汇总分享那些常被我们无意间忽视却又导致警告的编程小细节,以此作为对未来的自我警示和提升。
449 15

热门文章

最新文章