20240509 C加加复习(五)
明昧 Lv7

实现字符串类,怎样设计拷贝、赋值、析构函数

字符串类的实现

拷贝、赋值、析构

  • 拷贝构造(函数名称和类名相同),参数是自己。需要分配内存,将被拷贝的类的内存拷贝到分配的内存里
  • 拷贝赋值:赋值,参数是自己。先自我赋值校验,通过后释放原来指向的内存,最后分配内存,将被拷贝的类的内存拷贝到分配的内存里
  • 析构函数:释放指针指向的内存区域

赋值运算或者表达式返回值是引用

  • 从两个问题来进行分析返回值问题和引用问题

1.首先,赋值表达式为什么要有返回值呢?为了支持链式的复制表达式!

1
2
3
4
5
6
int a,b;
a = b = 1;

// 等号运算符是右结合的,相当于:
a = (b = 1);

其实是把b=1的返回值赋给了a,所以赋值表达式要有返回值。

2.为什么不返回值,而是返回引用呢?为了效率!

而且通常返回常量引用。比如你的赋值变量不是基本数据类型,而是对象时:

如果b=c返回的是值,则需要在=运算结束后,调用拷贝构造函数将结果复制为返回值(因为函数结束后栈空间是要被销毁的,而局部变量存储在栈空间中)。如果返回引用,则不需要调用复制返回值,直接将引用原有的变量。

编译器为类默认生成的这几个

1
2
3
4
5
A()            //默认构造函数
A(const A&) //默认拷贝构造函数
~A() //默认析构函数
A& operator = (const A &) //默认赋值函数。

不同构造函数的调用时机

拷贝初始化 A y=x;//拷贝初始化,调用拷贝构造函数

直接初始化 A x(2);//直接初始化,调用构造函数

思路

1.存储的开辟和管理(需要一个指向字符串地址的指针,当前字符串的长度,当前字符串所开辟空间的长度在我看来至少需要这三个要素

2.字符串本身基本概念的理解和设计

3.深拷贝和浅拷贝的实现和理解

4.拷贝函数、赋值函数、析构函数一般需要实现的功能

5.字符串是怎样被C++流给读取的和识别的,在自定义中如何实现(感觉这个涉及得更深一点就要到输入输出了

6.特殊情况的处理(空指针和nullptr)


思考关键点:

code

1
2
3
4
5
6
7
8
9
10
class String
{
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
~ String(void); // 析构函数
String & operator =(const String &other); // 赋值函数
private:
char *m_data; // 用于保存字符串
};

普通构造传的是指针

拷贝构造传的是引用

赋值函数传的是引用


首先了解形参:形参是实参额拷贝.

如果传值,那么形参会拷贝一份实参,用在对象上就是调用类的拷贝构造函数拷贝一个新的对象.

for example:

date (const date d1)//这里传的是值而不是引用.

//那么形参d1就会调用类的拷贝构造函数date(const date d1)

复制一份

// d1,现在这一层的d1又会再次调用拷贝构造函数,与刚才第一部的过程一致,

// 层层循环,无穷调用,导致程序崩溃.

3.那么传引用有什么用呢,首先要知道引用是什么,它是变量的别名,存变量的地址,通过它可以直接找到变量,然后直接对变量本身进行修改.如果传入一个对象的引用,那就不会调用它的拷贝构造函数.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//普通构造函数
String::String(const char *str)
{
if(str==NULL)
{
m_data = new char[1]; // 得分点:对空字符串自动申请存放结束标志'\0'的空
//加分点:对m_data加NULL 判断
*m_data = '\0';
}
else
{
int length = strlen(str);
m_data = new char[length+1]; // 若能加 NULL 判断则更好
strcpy(m_data, str);
}
}


// String的析构函数
String::~String(void)
{
delete [] m_data; // 或delete m_data;
}


//拷贝构造函数
String::String(const String &other)    // 得分点:输入参数为const型
{
int length = strlen(other.m_data);
m_data = new char[length+1];     //加分点:对m_data加NULL 判断
strcpy(m_data, other.m_data);
}


//赋值函数
String & String::operator =(const String &other) // 得分点:输入参数为const型
{

if(this == &other)   //得分点:检查自赋值
return *this;

delete [] m_data;     //得分点:释放原有的内存资源
int length = strlen( other.m_data );
m_data = new char[length+1];  //加分点:对m_data加NULL 判断
strcpy( m_data, other.m_data );
return *this;         //得分点:返回本对象的引用
}

构造函数

构造函数的任务,就是初始化对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。

  • 分类:

隐式创建/默认构造函数

显式创建/带参数的默认构造函数


1.默认的还好,主要就是涉及一个新空间的开辟是否是必须的问题(默认情况下是‘/0’状态还是nullptr状态)

2.如果是带参数的话,就是第一个是判断传入的参数是否是合法的,第二个就是处理传入的参数的相关内容

3.涉及到多种不同的带参形式的话,要对于所有可能传入的参数类型和组合进行分析,以便得出最优的参数设计组合


赋值函数

 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
Unique Visitor Page View