C++ class with pointer member(s)
- 正如标题所示:这篇复习带有指针类型成员的class
设计类
考虑到会有以下操作,来设计类
{
String s1();
String s2("hello");
String s3(s1);
cout << s3 << endl;
s3 = s2;
cout << s3 << endl;
}
函数体内第二行和第三行都是构造函数,一个含参数,一个不含参数。第四行创建一个以s1为初值的对象s3,是一个拷贝的动作,需要一个拷贝构造函数,之后会讲到;下一行是输出,需要一个操作符重载。第六行是一个赋值的操作,是一个拷贝的动作,这样第四行和第六行都是拷贝动作,所以这两个操作需要的是不同的函数,第六行需要的是拷贝赋值操作。如果,我们自己不写,编译器会给出默认的这两个操作函数,像上个复数的例子,就使用编译器给的,而这个string的例子,使用默认的就会出现不好的,想象一下,我们现在有个指针对象,指向一个地址,现在又创建一个新的对象,若只是拷贝,把指针拷贝过来,指向了同一个地方去,这并不是真正的拷贝,所以,只要class中含有带指针的成员,就不要使用默认的拷贝构造函数和拷贝赋值操作。so,string类的大致设计如下:
class String {
public:
String(const char* cstr = );//construct func
//if only the calss with pointer pamater(s),we need design two functions as follow
String(const String& str);//copy construct func,the parameter type is itsown type
String& operator=(const String& str);//copy assign,the parameter is itsown type
~String();
char* get_c_str()const { return m_data; }
private:
char* m_data;//因为字符串长度不定,所以设置成动态的数组-指针
};
设计类内函数
构造函数和析构函数
inline
String::String(const char* cstr = )
{
if (cstr) {//有初值
m_data = new char[strlen(cstr + )];
strcpy(m_data, cstr);
}
else {
m_data = new char[];
*m_data = '\0';
}
} inline
String::~String()
{
delete[] m_data;//clean up
}
使用上述函数
{
String s1();
String s2("hello");
String* p = new String("hello");
delete p;//离开前,释放掉
}
前面两个,离开时会自动释放掉,也是调用析构函数,所以上述函数作用域内离开时会调用三次析构函数
先前的复数类,不需要清理,因为它们本来就要死亡了,所以不必要,但是,这里是动态分配内存,如果不释放的,就是内存泄漏了。so ,Note:如果class中含指针成员,多半要动态分配内存,那么对象死亡之前,就是析构函数调用时,将动态分配的内存释放掉
拷贝构造函数和拷贝赋值操作
class with pointer member(s),一定要这样做,看下面的图片(来源于侯捷老师的课件)

首先,看第一种情况(第二幅图),使用default copy construct function,对象的data是指针,至于指针中的内容(‘hello’)是不属于这个指针的,在做copy动作的时候,b也也只是指针,所以两个指针指向同一块内容了。虽然a和b 也有相同内容了,但是b中内容,没有指针指向它了,而此处‘hello‘’所在的内存块,有两个指针同时指向它了,将来若改变a,b指向的内容也会被改变。,着就是浅拷贝;与之对应的就是深拷贝,就是我们自己写的函数要做的操作.下面看看什么是深拷贝
//copy construct fuction
inline
String::String(const String& str)
{
m_data = new char[strlen(str.m_data) + ];
strcpy(m_data, str.m_data);
}
使用:
String s3(s1);
这里s3也是新创建出来的对象,就要调用构造函数,先开辟足够的空间,然后将要拷贝的内容拷贝进来,这就是深拷贝,如果使用编译器默认的拷贝构造函数,只是把指针拷贝过来
拷贝赋值操作操作
要把右边的东西赋值给左边(Note:左右都有东西),正常思路就是先把左边清空,然后创建出与右边一样大的空间,再将右边内容拷贝给左边,so,实现如下:
inline
String& String::operator=(const String& str)
{
if (this == &str)//self assignment or not
return *this;
delete[] m_data;//kill
m_data = new char[strlen(str.m_data) + ];
strcpy(m_data, str.m_data);
return *this;
}
//使用
String s4=s1;
output 函数
#include <iostream>
#include "string.h" using namespace std;
ostream& operator<<(ostream& os, const String& str)
{
os << str.get_c_str();
return os;
}
//使用 String s1("Hello");
cout<<s1;
output 函数,必须为全域函数,这样才能保证是左边调用“<<”
总结
下面,我们回顾一下String 类的设计,设计一个class,首先,我们考虑class中需要什么样的数据,这里是字符串,我们字符串中会存放很多字符,当然,我们很容易想到使用数组存放,但是,对于设计字符串却不是一个好的选择,因为,数组声明时必须要指定数组大小,所以,我们选择指针,将来放多大的内容,使用new的方式动态分配大小,在32位系统中,一个指针占4个byte,所以不管字符串多大,字符串这个对象本身就是4byte;Then,考虑设计哪些函数,构造函数,前面讲过了,这里不多讲;上面讲过了,因为成员是指针,所以需要设计拷贝构造函数、拷贝赋值操作函数,析构函数(Big Three),设计完这三个函数后,再思考还需要设计哪些函数,由于,我们需要输出字符串,即需要cout,,所以,我们需要取出m_data中的字符,cout是可以接收这种东西的,所以,设计 char* get_c_str() const {return m_data;} ,只是返回,不改变,所以设计成const型的。
下面是完整的String类的设计的头文件
#pragma once
#ifndef __STRING__
#define __STRING__
#include <cstring>
class String {
public:
String(const char* cstr = );//construct func
//if only the calss with pointer pamater(s),we need design two functions as follow
String(const String& str);//copy construct func,the parameter type is itsown type
String& operator=(const String& str);//copy assign,the parameter is itsown type ~String();
char* get_c_str()const { return m_data; }
private:
char* m_data;//因为字符串长度不定,所以设置成动态的数组-指针
}; inline
String::String(const char* cstr = )
{
if (cstr) {//有初值
m_data = new char[strlen(cstr + )];
strcpy(m_data, cstr);
}
else {
m_data = new char[];
*m_data = '\0';
}
} inline
String::~String()
{
delete[] m_data;//clean up
}
//copy construct fuction
inline
String::String(const String& str)
{
m_data = new char[strlen(str.m_data) + ];
strcpy(m_data, str.m_data);
} inline
String& String::operator=(const String& str)//String&:& is reference
{
if (this == &str)//self assignment or not,&str:& is getting address
return *this;
delete[] m_data;//kill
m_data = new char[strlen(str.m_data) + ];
strcpy(m_data, str.m_data);
return *this;
}
#endif
C++ class with pointer member(s)的更多相关文章
- 漫谈C++:良好的编程习惯与编程要点
以良好的方式编写C++ class 假设现在我们要实现一个复数类complex,在类的实现过程中探索良好的编程习惯. ① Header(头文件)中的防卫式声明 complex.h: # ifndef ...
- CLR via C# 3rd - 04 - Type Fundamentals
1. System.Object The runtime requires every type to ultimately be derived from the System.Obj ...
- 《深度探索C++对象模型(Inside The C++ Object Model )》学习笔记
转载:http://dsqiu.iteye.com/blog/1669614 第一章 关于对象 使用class封装之后的布局成本: class并没有增加成本,data members直接内含在每一个c ...
- c语言实现面向对象OOC
这种问题比较锻炼思维,同时考察c和c++的掌握程度.如果你遇到过类似问题,此题意义自不必说.如果用c实现c++,主要解决如何实现封装,继承和多态三大问题,本文分两块说. 1.封装 // Example ...
- C++ 表达式
<C++ Primer 4th>读书摘要 C++ 提供了丰富的操作符,并定义操作数为内置类型时,这些操作符的含义.除此之外,C++ 还支持操作符重载,允许程序员自定义用于类类型时操作符的含 ...
- iOS:消除项目中警告
引言: 在iOS开发过程中, 我们可能会碰到一些系统方法弃用, weak.循环引用.不能执行之类的警告. 有代码洁癖的孩子们很想消除他们, 今天就让我们来一次Fuck 警告!! 首先学会基本的语句: ...
- 4、Type fundamentals
1.All Types Are Derived from System.Object The CLR requires all objects to be created using the new ...
- 【转】clang warning 警告清单(备查,建议直接command + F 速查 )
Warning Message -WCFString-literal input conversion stopped due to an input byte that does not belon ...
- 使用#pragma阻止一些warnings
这篇博客的内容都是记的网上的.是流水账.只是记录下来以便日后之有,避免每次重新google. #pragma除了可以用来把不同功能的代码进行分隔组织外还可以用来disable一些warnings.这在 ...
随机推荐
- iOS 优化ipa包,减小安装包大小
https://www.jianshu.com/p/a49d59b01669 项目打包之后.ipa包的大小是118.9M,上传到App Store后iPhone6s上显示85.5M,下载时间太长,所以 ...
- 验证码比较hash_equals 方法
验证码是否与缓存中一致时,使用了 hash_equals 方法: hash_equals($verifyData['code'], $request->verification_code) ha ...
- 7、Maven插件
什么是maven插件? maven 实际上是一类依赖插件执行的框架,每个任务实际上是由插件完成,Maven插件通常被用来 创建jar文件 创建war文件 编译代码文件 代码单元测试 创建工程文档 创建 ...
- github初使
怎么说那,全英文,对于我这个英文水平不是很高的人来说有一定的影响,但是这也促使了我学习英语,而且里面一些大牛的发表也不少的是英文版的,我感觉我在英语方面的需求,由github来提升了,早就注册好了账号 ...
- 树莓派下编译并使用miracl密码库
参考:Linux下编译并使用miracl密码库 MIRACL用户手册:https://wenku.baidu.com/view/d542f2ed0975f46527d3e1dc.html 具体过程. ...
- cc攻击怎么防御,如何防止cc攻击?
当我们访问一个网站时,如果网站页面越简单,访问速度越快,页面越漂亮,加载速度就越慢,因为要加载更多东西,服务器压力也会比较大.cc攻击就是利用这种弱点,使用大量代理服务器,对网站进行攻击,消耗网站服务 ...
- Python socket day2
接收数据 需要一个端口 (端口大于1024 1024一下的端口为特殊端口) (当同一个端口同一时间只能被一个使用) 创建一个套接字 s = socket.socket(AF_INET,SOCK ...
- LeetCode练题——66. Plus one
1.题目 66. Plus One——easy Given a non-empty array of digits representing a non-negative integer, plus ...
- Java学习资源 - J2SE
java.lang包教程 Java集合类详解 Java回顾之集合 Java回顾之序列化 Java回顾之反射 深入理解Java:类加载机制及反射 Java 下高效的反射工具包 ReflectASM 使用 ...
- Http 状态码总结
HTTP 状态码列表 一. 1 开头 (继续执行) 服务器收到请求,需要请求者继续执行操作 100:(continue) 客户端继续请求 101:(Switching Protocols) 切换协议, ...