您的位置:首页 > 编程语言 > C语言/C++

类中有指针成员变量时的一些注意事项

2015-04-15 21:29 253 查看
知识真的是知道的越多,不知道到的越多。本来只是想在自己写的string类中加个记录对象的静态成员变量,刚开始遇到了计数结果为负值的情况,后来知道了有复制构造函数这么一说。有因为类中声明了一个str的指针变量,导致了一些内存问题,于是迫使自己重载了构造函数,析构函数,甚至还知道并重载了赋值运算符。后来,在网上系统的查了下,原来一个类在创建时候,编译器会自动生成默认构造函数(若没有),复制构造函数(若没有),默认析构函数(若没有),赋值运算符(若没有),取地址运算符(若没有)。以下列出了自己最终版本的string类,为了方便对照查看,将说明写在了代码的注释里。

//mystring.h
#ifndef MYSTRING_H_
#define MYSTRING_H_

#include <iostream>
using std::ostream;

class mystring
{
private:
char* str;
int len;
static int num;//对存在的对象进行计数
public:
mystring();//默认构造函数
mystring(char* c);//带参构造函数
mystring(const mystring& mystr);//复制构造函数
~mystring();//析构函数
char& operator[](int i);//重载[]运算符----非const对象调用
const char& operator[](int i) const;//重载[]运算符----const对象调用,函数重载区分const与非const对象
mystring& operator=(mystring& mystr);//重载赋值运算符
friend ostream& operator<<(ostream& os,const mystring& mystr);
operator char*();
};
#endif
//mystring.cpp
#include <string>
#include <iostream>
#include "mystring.h"

using std::cout;
using std::endl;

int mystring::num = 0;//初始化静态变量,不用加static,但需要加类型

mystring::mystring()//默认构造函数,生成空串
{
len = 0;
// 	str = new char[1];//对应析构函数的delete[]
// 	str[0] = '\0';
str = 0;//空指针同样也用delete[]释放
num++;
cout<<"调用默认构造函数,第"<<num<<"个对象"<<endl;
}

mystring::mystring(char* c)
{
len = strlen(c);
str = new char[len + 1];
strcpy(str,c);
num++;
cout<<str<<":调用带参数构造函数,第"<<num<<"个对象"<<endl;
}

mystring::mystring(const mystring& mystr)//对使用复制构造函数创建的对象进行深度复制,否则,比方说创建的临时对象析构时会会把内存中的内容释放,指针指向的内容将不复存在
{
len = mystr.len;
str = new char[len + 1];
strcpy(str,mystr.str);
num++;
cout<<str<<":调用复制构造函数,第"<<num<<"个对象"<<endl;
}

mystring::~mystring()
{
num--;
if(str == 0)//空指针
cout<<"空对象调用析构函数,剩余"<<num<<"个对象"<<endl;
else
cout<<str<<":调用析构函数,剩余"<<num<<"个对象"<<endl;
delete[] str;//释放对象str占用的内存
}

mystring& mystring::operator =(mystring& mystr)//默认赋值运算符是浅度复制,只是将指针复制过去,指向的内容并没有复制。这并不符合赋值的初衷,而且一处的修改会影响其他的对象的str
{
if(this == &mystr)
return *this;

delete[] str;
len = mystr.len;
str = new char[len + 1];
strcpy(str,mystr.str);

return *this;//返回对象引用可进行连续赋值
}

char& mystring::operator [](int i)//非const对象
{
return str[i];//其本质是返回一个地址,使用其对对象数据进行修改
}

const char& mystring::operator [](int i) const//const对象调用,也可返回非const引用,但可通过引用对const对象进行修改,不科学
{
return str[i];//若不返回const,可利用其返回的地址对const对象进行修改,类中的数据,即使是私有成员也可以通过地址进行访问或者修改
}

ostream& operator<<(ostream& os,const mystring& mystr) //必须返回ostream对象的引用,因为没有公有的复制构造函数(不能创建临时对象)
{
os<<mystr.str<<"是长度为"<<mystr.len<<"的字符串";
return os;//使连续输出成为可能,
}

mystring::operator char*()//将类转化为其他类型
{
return str;
}
//test.cpp
#include <iostream>
#include "mystring.h"
using namespace std;

int main()
{
mystring str1;
mystring str2("hehe");
const mystring str3("const hehe");
str2[1] = 'a';
str2[3] = 'a';//调用非const重载[],修改str2
//str3[1] = 'a';//报错,返回类型为const不可修改
mystring* pStr = new mystring(str2);//用str2初始化一个新的对象指针,区别于mystring* pStr = &str2
str1 = *pStr;//取pStr值然后赋值给str1,调用重载=运算符,new生成的对象,必须用delete调用析构函数释放内存
delete pStr;//析构pStr
cout<<str3<<"!!!"<<endl;
mystring s = "???";//使用的隐式的类型转换,单参数的构造函数的功能,与s = mystring("???")等效。
cout<<s<<"!!!"<<endl;
char* p = str2;//使用隐式类型转换,与char* p =char*(str2)等效
cout<<p<<endl;
return 0;
}

最终的运行结果如下:



总结:测试程序中用几种不同的方式创建了mystring 对象,使用num对其进行计数,并打印出了构造函数等调用的时机,大家可以对照查看,或者自己写代码测试。本文主要是想说明在对有指针变量的类中,进行复制赋值操作时,要注意深度复制赋值。顺带着介绍了转换函数(将类转换成其他类型变量,或者将其他类型变量转换成类变量)的语法。其中注释中都有详细介绍在此就不再赘述。

由于本人才疏学浅,若有错误,欢迎指正,不胜感激。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐