• 正如标题所示:这篇复习带有指针类型成员的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)的更多相关文章

  1. 漫谈C++:良好的编程习惯与编程要点

    以良好的方式编写C++ class 假设现在我们要实现一个复数类complex,在类的实现过程中探索良好的编程习惯. ① Header(头文件)中的防卫式声明 complex.h: # ifndef ...

  2. CLR via C# 3rd - 04 - Type Fundamentals

    1. System.Object        The runtime requires every type to ultimately be derived from the System.Obj ...

  3. 《深度探索C++对象模型(Inside The C++ Object Model )》学习笔记

    转载:http://dsqiu.iteye.com/blog/1669614 第一章 关于对象 使用class封装之后的布局成本: class并没有增加成本,data members直接内含在每一个c ...

  4. c语言实现面向对象OOC

    这种问题比较锻炼思维,同时考察c和c++的掌握程度.如果你遇到过类似问题,此题意义自不必说.如果用c实现c++,主要解决如何实现封装,继承和多态三大问题,本文分两块说. 1.封装 // Example ...

  5. C++ 表达式

    <C++ Primer 4th>读书摘要 C++ 提供了丰富的操作符,并定义操作数为内置类型时,这些操作符的含义.除此之外,C++ 还支持操作符重载,允许程序员自定义用于类类型时操作符的含 ...

  6. iOS:消除项目中警告

    引言: 在iOS开发过程中, 我们可能会碰到一些系统方法弃用, weak.循环引用.不能执行之类的警告. 有代码洁癖的孩子们很想消除他们, 今天就让我们来一次Fuck 警告!! 首先学会基本的语句: ...

  7. 4、Type fundamentals

    1.All Types Are Derived from System.Object The CLR requires all objects to be created using the new ...

  8. 【转】clang warning 警告清单(备查,建议直接command + F 速查 )

    Warning Message -WCFString-literal input conversion stopped due to an input byte that does not belon ...

  9. 使用#pragma阻止一些warnings

    这篇博客的内容都是记的网上的.是流水账.只是记录下来以便日后之有,避免每次重新google. #pragma除了可以用来把不同功能的代码进行分隔组织外还可以用来disable一些warnings.这在 ...

随机推荐

  1. PHP中PHP $_POST和PHP $_REQUEST及PHP $_GET的用法及区别

     笔者最近开始学习PHP语言大法,记录一下学习过程中遇到的知识点.          今天介绍的是php中有关 php $_post 和 php $_request 及 php $_get 的用法及区 ...

  2. ubuntu16 安装opencv3.4.2

    下载好opencv3.4.2.zip 执行命令: unzip opencv3.4.2.zip 进入解压后的文件夹: cd opencv3.4.2/ 创建编译路径: mkdir release 进入新创 ...

  3. 【PAT甲级】1094 The Largest Generation (25 分)(DFS)

    题意: 输入两个正整数N和M(N<100,M<N),表示结点数量和有孩子结点的结点数量,输出拥有结点最多的层的结点数量和层号(根节点为01,层数为1,层号向下递增). AAAAAccept ...

  4. Ubuntu 18 设置静态 IP

    在Ubuntu 18中使用 netplan 命令 首先 cd /etc/netplan/ 找到 *.yaml文件(每个人可能不一样),编辑它 root@waydeserver:/etc/netplan ...

  5. left join 、right join 和inner join之间的区别

    SQL的left join .right join 和inner join之间的区别 left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录  right join(右联接) ...

  6. Spring Boot Ftp Client 客户端示例支持断点续传

    本章介绍 Spring Boot 整合 Ftpclient 的示例,支持断点续传 本项目源码下载 1 新建 Spring Boot Maven 示例工程项目 注意:是用来 IDEA 开发工具 File ...

  7. 洛谷P1346 电车(需要稍加思索的最短路)

    题目描述 在一个神奇的小镇上有着一个特别的电车网络,它由一些路口和轨道组成,每个路口都连接着若干个轨道,每个轨道都通向一个路口(不排除有的观光轨道转一圈后返回路口的可能).在每个路口,都有一个开关决定 ...

  8. Codeforces Round #611 (Div. 3) C

    There are nn friends who want to give gifts for the New Year to each other. Each friend should give ...

  9. java 关于多层的异常捕获

    从这两个源程序可以看出来,这里的逻辑其实很好理清楚. 可以看到,每次抛出了相同的错误,但因为catch后的捕捉类型不同,所以结果不同,其实可以看出多层的异常捕捉和正常的多层代码运行逻辑是基本一致的.

  10. 学校实训作业:Java爬虫(WebMagic框架)的简单操作

    项目名称:java爬虫 项目技术选型:Java.Maven.Mysql.WebMagic.Jsp.Servlet 项目实施方式:以认知java爬虫框架WebMagic开发为主,用所学java知识完成指 ...