析构函数
#include<iostream>
#include<string>
#include<vector>
using namespace std;
/*
如果类中有数据指针成员且构造函数中使用new初始化,那么必须自行写析构函数;
如果写了析构函数,那么一定要写复制构造函数和赋值函数。
*/
class NoName
{
public:
NoName() :pstring(new std::string), i(0), d(0) {}
~NoName();// declaration of destruction
// 定义复制构造函数
NoName(const NoName& other)
:pstring(new std::string(*(other.pstring))),
i(other.i),
d(other.d)
{}
// 定义赋值函数
NoName& operator=(const NoName& rhs)
{
pstring = new std::string;
*pstring = *rhs.pstring;
i = rhs.i;
d = rhs.d;
}
private:
std::string* pstring; // pointer
int i;
double d;
};
NoName::~NoName()
{
cout << "析构函数被调用" << endl;
delete pstring;
}
int main()
{
NoName a;
NoName* p = new NoName;
cout << "Hello\n" << endl;
delete p; // 删除p指向的NoName对象的数据指针成员
cout << "ok,delete p" << endl;
return 0;//第二次调用析构函数删除对象a中的数据指针成员你
}
复制构造函数:浅复制
#include <iostream>
#include<string>
using namespace std;
class CDemo
{
public:
CDemo(int pa, const char* cstr)
{
this->a = pa;
this->str = new char[1024];
strcpy_s(this->str,1024, cstr);
}
// C++自动生成的复制构造函数大概是这样的
CDemo(CDemo & obj)
{
this->a = obj.a;
this->str = obj.str; //浅复制
}
public:
int a;
char* str;
};
/*
深复制,复制的是内容;
浅复制,复制的是内容所在存储空间的地址;
*/
int main()
{
CDemo A(10, "hello");
CDemo B=A;
//如果自己没有写复制构造函数,那么这里会调用C++的复制构造函数(执行浅复制)
B.a = 8;
//TEST 这确实是一个浅复制
B.str[0] = 'k';
cout <<"A="<<A.a<<"," << A.str << endl; // 结果发现A中的str被修改
}
/*
对象是两个,但是对象所引用的资源是只有一个;
此时,如果其中一个对象被析构,那么所指向的资源也会被释放,如此一类,另外一个对象的指针会变成野指针
*/
复制构造函数:深复制
#include <iostream>
#include<string>
using namespace std;
class CDemo
{
public:
CDemo(int pa, const char* cstr)
{
this->a = pa;
this->str = new char[1024];
strcpy_s(this->str,1024, cstr);
}
// C++自动生成的复制构造函数大概是这样的
CDemo(CDemo & obj)
{
this->a = obj.a;
this->str = new char[1024]; //深复制
strcpy_s(this->str, 1024, obj.str);
}
~CDemo()
{
delete str;
}
public:
int a;
char* str;
};
/*
深复制,复制的是内容;
浅复制,复制的是内容所在存储空间的地址;
*/
int main()
{
CDemo A(10, "hello");
CDemo B=A;
//如果自己没有写复制构造函数,那么这里会调用C++的复制构造函数(执行浅复制)
B.a = 8;
//TEST 这确实是一个深复制
B.str[0] = 'k';
cout <<"A="<<A.a<<"," << A.str << endl; // 结果发现A中的str没有被修改
}
管理指针成员
智能指针
深复制会产生大量对象,有时候我们希望若干指针指向同一个空间,但是又不希望
/*plain_ptr.h头文件内容是这样的*/
//#pragma once
// 浅复制
class AHasptr {
public:
AHasptr(int *p,int i):ptr(p),val(i){}
int* get_ptr() const { return ptr; }
int get_int() const { return val; }
void set_ptr(int* p) { ptr = p; }
void set_int(int i) { val = i; }
int get_ptr_val() const { return *ptr; }
void set_ptr_val(int val) const
{ *ptr = val;}//why it can do. const, ans : we cannot modify the address
private:
int val;
int* ptr;
};
下面测试在源文件中使用头文件定义的类,感受浅复制。。
#include <iostream>
#include "plain-ptr.h"
using namespace std;
void test_AHasPtr()
{
int i = 42;
AHasptr p1(&i, 42);
AHasptr p2 = p1;
cout << p2.get_ptr_val() << endl;
// now, modify the int_value to which p1's poninter point
p1.set_ptr_val(2);
// then, find the value of p2 has changed
cout << p2.get_ptr_val() << endl;
}
int main()
{
test_AHasPtr();
int* ip = new int(42);
AHasptr ptr(ip, 10);
cout << ptr.get_ptr_val() << endl;
delete ip;
//此时对象ptr中的指针成员会变成野指针,指向一个无效的地址
cout << ptr.get_ptr_val() << endl;
return 0;
}
深拷贝的例子
头文件的代码如下
//#pragma once
// 深拷贝的类-头文件
class CHasPtr
{
public:
CHasPtr(int p, int i) // 使用引用,创建了一个新空间接受这个值
:ptr(new int(p)), val(i) {}
int* get_ptr() const { return ptr; }
int get_int() const { return val; }
void set_ptr(int* p) { ptr = p; }
void set_int(int i) { val = i; }
int get_ptr_val() const { return *ptr; }
void set_ptr_val(int i) const { *ptr = i; }
// 深复制需要自行编写复制构造函数
CHasPtr(const CHasPtr& orig)
{
this->val = orig.val;
this->ptr = new int(*(orig.ptr));
}
//深复制需要自行编写赋值重载函数
CHasPtr& operator=(const CHasPtr& orig)
{
CHasPtr temp(*(orig.ptr), orig.val);
return *this;
}
//如果自行编写复制构造函数,那么也应该编写析构函数
~CHasPtr()
{
delete ptr;
}
private:
int val;
int* ptr;
};
在源文件中,体会深拷贝……
#include <iostream>
#include "plain-ptr.h"
#include "value-ptr.h"
using namespace std;
void test_AHasPtr()
{
int i = 42;
AHasptr p1(&i, 42);
AHasptr p2 = p1;
cout << p2.get_ptr_val() << endl;
// now, modify the int_value to which p1's poninter point
p1.set_ptr_val(2);
// then, find the value of p2 has changed
cout << p2.get_ptr_val() << endl;
int* ip = new int(42);
AHasptr ptr(ip, 10);
cout << ptr.get_ptr_val() << endl;
delete ip;
//此时对象ptr中的指针成员会变成野指针,指向一个无效的地址
cout << ptr.get_ptr_val() << endl;
}
void test_CHasPtr()
{
cout << "deep copy" << endl;
int obj = 0;
CHasPtr ptr1(obj, 10);
CHasPtr ptr2(ptr1);
cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;
cout << " now, modify ptr1's-value" << endl;
ptr1.set_ptr_val(20);
cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;
}
int main()
{
test_AHasPtr();
cout << endl;
test_CHasPtr();
return 0;
}
下面是输出结果,可以看到修改ptr1,但是ptr2的值并没有发生改变
深复制,又被称作值型类,这是因为借鉴了整数型变量的复制,比如整数型变量的复制,一个变量的修改就不会影响到另外一个。
然而,深复制每个指针都有一个对象,有时候会占用大量内存,因此考虑使用智能指针
自己写智能指针类
new一个智能指针
智能指针里面有一个普通指针指向公用对象
计数,计算有多少对象指针使用这个共用对象
如果计数不为0,意味着有对象在引用公用对象是,那么就不能删除这个共用对象;
如果为0,可以删除
可以根据计数,对指针进行管理
可以看到智能指针对象里面的指针成员地址是一样的,如果修改一个,另一个也会发生变化。
#pragma once
//自行编写一个智能指针类
/*
ptr-ip,表示智能指针里面有一个普通指针
复制构造函数的调用,使得其多了一个对象使用、指向共用对象空间,因此也要++
++ptr->use
反过来,析构要--,如果计数为0,那么就把它删除掉
改一个对象,就会修改到公用对象的内容
智能指针通过计数,避免了野指针,
注:在vs2019,会出现重复删除的问题……
*/
class U_Ptr
{
private:
int* ip;
size_t use; // to conut
U_Ptr(int *p):ip(p),use(1){}
~U_Ptr() { delete ip; }
friend class BHasPtr;
};
/*暂时的理解:BHasPtr中的成员指针是智能指针(U_Ptr),智能指针里面是普通指针*/
class BHasPtr
{
public:
BHasPtr(int* p,int i)//:ptr(new U_Ptr(p),val(i))
{
ptr = new U_Ptr(p);
val = i;
}
BHasPtr(const BHasPtr &orig):ptr(orig.ptr),val(orig.val)
{
++ptr->use;
}
~BHasPtr()
{
if (--ptr->use == 0)
delete ptr; // why not ptr->ip
}
int* get_ptr() const { return ptr->ip; }
int get_int() const { return val; }
void set_ptr(int* p) { ptr->ip = p; }
void set_int(int i) { val = i; }
int get_ptr_val() {return *(ptr->ip); } // ptr是一个指针,指向U_Ptr对象,可以把它U_Ptr类中的this
void set_ptr_val(int val) { *(ptr->ip) = val; }
BHasPtr& operator=(const BHasPtr& rhs)
{
++rhs.ptr->use;
if (--ptr->use == 0) delete ptr;
ptr = rhs.ptr;
val = rhs.val;
return *this;
}
private:
int val;
U_Ptr* ptr;
};
测试智能指针的源文件
#include <iostream>
#include "plain-ptr.h"
#include "value-ptr.h"
# include "smart-ptr.h"
using namespace std;
void test_AHasPtr()
{
int i = 42;
AHasptr p1(&i, 42);
AHasptr p2 = p1;
cout << p2.get_ptr_val() << endl;
// now, modify the int_value to which p1's poninter point
p1.set_ptr_val(2);
// then, find the value of p2 has changed
cout << p2.get_ptr_val() << endl;
int* ip = new int(42);
AHasptr ptr(ip, 10);
cout << ptr.get_ptr_val() << endl;
delete ip;
//此时对象ptr中的指针成员会变成野指针,指向一个无效的地址
cout << ptr.get_ptr_val() << endl;
}
void test_CHasPtr()
{
cout << "deep copy" << endl;
int obj = 0;
CHasPtr ptr1(obj, 10);
CHasPtr ptr2(ptr1);
cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;
cout << " now, modify ptr1's-value" << endl;
ptr1.set_ptr_val(20);
cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;
}
void test_BHasPtr()
{
int obj = 0;
BHasPtr ptr1(&obj, 42);
BHasPtr ptr2(ptr1);
cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;
cout << " now, modify ptr1's-value" << endl;
ptr1.set_ptr_val(20);
cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;
}
int main()
{
cout<<"常规指针,浅拷贝:" << endl;
test_AHasPtr();
cout << endl;
test_CHasPtr();
cout << endl;
cout << "智能指针:" << endl;
test_BHasPtr();
return 0;
}
运算符重载
重载操作符不会改变优先级,
重载操作函数,可以是成员函数,也可以是友元函数;
#include <iostream>
#include<string>
using namespace std;
class Sales_item
{
friend ostream& operator<<(ostream& out, Sales_item& item);
friend istream& operator>>(istream& in, Sales_item& item);
public:
Sales_item(const string &book,unsigned count, double price)
:isbn(book),units_sold(count),revenue(count*price){}
Sales_item():units_sold(0),revenue(0){}
Sales_item& operator+=(const Sales_item& item);
private:
string isbn;
unsigned units_sold;
double revenue;
};
ostream& operator<<(ostream& out, Sales_item& item)
{
out << item.isbn << "\t" << item.units_sold << "\t" << item.revenue << endl;
return out;
}
// generally , operator+= is a member function, operator+ is a friend function;
Sales_item& Sales_item::operator+=(const Sales_item& item) // operator+= 更新当前对象,因此函数的返回值是引用
{
this->units_sold += item.units_sold;
this->revenue += item.revenue;
return *this;
}
Sales_item operator+(const Sales_item& item1, const Sales_item& item2)// operator+ 函数会返回一个新对象,因此不能返回类的引用
{
Sales_item itemsum(item1);
itemsum += item2;
return itemsum;
}
istream& operator>>(istream& in, Sales_item& item)
{
double price;
in >> item.isbn >> item.units_sold >> price;
if (in)
item.revenue = price * item.units_sold;
else // error occurs, we need to 保证对象的数据处在一致的状态
item = Sales_item();
return in;
}
int main()
{
Sales_item item1("001", 2, 40);
cout << item1 << endl;
cout << "operator>>:" << endl;
cin >> item1;
cout << item1;
cout << endl;
cout << "operator+_TEST: " << endl;
Sales_item item2("002", 4, 80);
Sales_item result;
result = item1 + item2;
cout << result << endl;
cout << "operator+=_TEST: " << endl;
item1 += item2;
cout << item1 << endl;
return 0;
}
关系操作符重载
沿用上面operator+=, operator+的思路:
把等于
与不等于
一起重载,如果等于
已经重载,那么考虑重载不等于
的函数中调用等于
;
#include <iostream>
#include<stdlib.h>
using namespace std;
class Date
{
public:
Date(int m=0,int d=0, int y=0):month(m),year(y),day(d){}
int operator==(const Date& dt) const;
int operator<(const Date& dt) const;
private:
int year, month, day;
};
int Date::operator==(const Date& dt) const
{
return (year == dt.year) && (month == dt.month) && (day == dt.day);
}
int operator!=(const Date& dt1, const Date& dt2)
{
return !(dt1 == dt2);
}
int Date::operator<(const Date& dt) const
{
if (year == dt.year)
{ if (month == dt.month)
return day < dt.day;
return month < dt.month;
}
else
return year < dt.year;
}
int main()
{
Date d1(2, 14, 2020);
Date d2(3, 4, 2020);
Date d3(2, 16, 2020);
if (d1 < d2)
cout << "d1 < d2 " << endl;
if (d1 != d3)
cout << "d1!=d3" << endl;
system("pause");
return 0;
}
#include <iostream>
#include<stdlib.h>
using namespace std;
class String
{
public:
String(const char* p)
{
p = p ? p : ""; // if p is a null-pointer, then we 把它化为空字符串;
ptrChar = new char[std::strlen(p) + 1];
strcpy_s(ptrChar, 10,p);
}
String& operator=(String const&); // 赋值操作符的重载
void print()
{
cout << ptrChar << endl;
}
private:
char* ptrChar;
};
String& String::operator=(String const& str)
{
if (std::strlen(str.ptrChar) != std::strlen(ptrChar))
{
char* don = new char[std::strlen(str.ptrChar) + 1];//delete[] ptrChar;
delete[] ptrChar;
ptrChar=don; //char* ptrChar = new char[std::strlen(str.ptrChar) + 1];
}
strcpy_s(ptrChar, 10,str.ptrChar);
return *this;
}
int main()
{
String s("dog");
String h("hello");
s.print() ;
// use operator= function
s = h;
s.print();
system("pause");
return 0;
}
下标操作符的重载
#include <iostream>
#include<stdlib.h>
using namespace std;
class String
{
public:
String(const char* p)
{
p = p ? p : ""; // if p is a null-pointer, then we 把它化为空字符串;
ptrChar = new char[std::strlen(p) + 1];
strcpy_s(ptrChar, 10,p);
}
char& operator[](std::size_t index);// throw(String); // 下标操作符的重载
char& operator[](std::size_t index) const;
void print()
{
cout << ptrChar << endl;
}
private:
char* ptrChar;
static String errorMessage; // 显示报错信息
};
String String::errorMessage("Subscript out of range!"); // initialize this static memeber
char& String::operator[](std::size_t index) // throw(String)
{
//if (index >= std::strlen(ptrChar))
//throw errorMessage;
return ptrChar[index];
}
// 下标操作符重载:需要两个成员函数,1可变成员函数,2常量成员函数
// reason is that 对常变量s2来说,只能调用const成员函数;
char& String::operator[](std::size_t index) const// throw(String)
{
//if (index>=std::strlen(ptrChar))
//throw errorMessage;
return ptrChar[index];
}
int main()
{
String s("dog");
String h("hello");
s.print() ;
// use operator[] function
cout << s[0] << endl;
s[0] = 'A';
s.print();
system("pause");
return 0;
}
emmm,我之后回来修正,这里为什么报错了
报错内容:0x78DDFC66 (ucrtbased.dll) (Project2.exe 中)处有未经处理的异常: 将一个无效参数传递给了将无效参数视为严重错误的函数。