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;


相关文章
|
27天前
|
安全 算法 C++
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits处理弱枚举和强枚举
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits处理弱枚举和强枚举
46 3
|
29天前
|
设计模式 程序员 C++
【C++ 泛型编程 高级篇】C++模板元编程:使用模板特化 灵活提取嵌套类型与多容器兼容性
【C++ 泛型编程 高级篇】C++模板元编程:使用模板特化 灵活提取嵌套类型与多容器兼容性
255 2
|
28天前
|
算法 C++ 开发者
【C++运算符重载】深入理解C++中的流运算符 >>和<<重载
【C++运算符重载】深入理解C++中的流运算符 >>和<<重载
36 0
|
5天前
|
C++
面向对象的C++题目以及解法2
面向对象的C++题目以及解法2
11 1
|
6天前
|
编译器 C++
C++编程之美:探索初始化之源、静态之恒、友情之桥与匿名之韵
C++编程之美:探索初始化之源、静态之恒、友情之桥与匿名之韵
17 0
|
7天前
|
存储 编译器 C++
【C++成长记】C++入门 | 类和对象(中) |拷贝构造函数、赋值运算符重载、const成员函数、 取地址及const取地址操作符重载
【C++成长记】C++入门 | 类和对象(中) |拷贝构造函数、赋值运算符重载、const成员函数、 取地址及const取地址操作符重载
|
15天前
|
存储 人工智能 机器人
【C++面向对象】C++图书管理系统 (源码)【独一无二】
【C++面向对象】C++图书管理系统 (源码)【独一无二】
|
21天前
|
存储 人工智能 BI
【C++面向对象】C++银行卡管理系统(源码+论文)【独一无二】
【C++面向对象】C++银行卡管理系统(源码+论文)【独一无二】
|
28天前
|
存储 移动开发 安全
【C/C++ 口语】C++ 编程常见接口发音一览(不断更新)
【C/C++ 口语】C++ 编程常见接口发音一览(不断更新)
22 0
|
28天前
|
算法 编译器 C++
【C++ 模板编程 基础知识】C++ 模板类部分特例化的参数顺序
【C++ 模板编程 基础知识】C++ 模板类部分特例化的参数顺序
21 0

热门文章

最新文章