C++解析(19):函数对象、关于赋值和string的疑问

0.目录

1.函数对象

2.重载赋值操作符

3.string类

4.小结

1.函数对象

编写一个函数:

  • 函数可以获取斐波那契数列每项的值
  • 每调用一次返回一个值
  • 函数可根据需要重复使用

实现功能:

#include <iostream>
#include <string>

using namespace std;

int fib()
{
    static int a0 = 0;
    static int a1 = 1;
    
    int ret = a1;
    
    a1 = a0 + a1;
    a0 = ret;
    
    return ret;
}


int main()
{
    for(int i=0; i<10; i++)
    {
        cout << fib() << endl;
    }
    
    cout << endl;
    
    for(int i=0; i<5; i++)
    {
        cout << fib() << endl;
    }
    
    return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out 
1
1
2
3
5
8
13
21
34
55

89
144
233
377
610

存在的问题——函数一旦开始调用就无法重来

  • 静态局部变量处于函数内部,外界无法改变
  • 函数为全局函数,是唯一的,无法多次独立使用
  • 无法指定某个具体的数列项作为初始值

函数对象:

  • 使用具体的类对象取代函数
  • 该类的对象具备函数调用的行为三个字
  • 构造函数指定具体数列项的起始位置
  • 多个对象相互独立的求解数列项

函数调用操作符(( )):
只能通过类的成员函数重载
可以定义不同参数的多个重载函数

最终解决方案——把类的对象当作函数使用:

#include <iostream>
#include <string>

using namespace std;

class Fib
{
    int a0;
    int a1;
public:
    Fib()
    {
        a0 = 0;
        a1 = 1;
    }
    
    Fib(int n)
    {
        a0 = 0;
        a1 = 1;
        
        for(int i=2; i<=n; i++)
        {
            int t = a1;
            
            a1 = a0 + a1;
            a0 = t;
        }
    }
    
    int operator () ()
    {
        int ret = a1;
    
        a1 = a0 + a1;
        a0 = ret;
        
        return ret;
    }
};

int main()
{
    Fib fib;
    
    for(int i=0; i<10; i++)
    {
        cout << fib() << endl;
    }
    
    cout << endl;
    
    for(int i=0; i<5; i++)
    {
        cout << fib() << endl;
    }
    
    cout << endl;
    
    Fib fib2(10);
    
    for(int i=0; i<5; i++)
    {
        cout << fib2() << endl;
    }
    
    return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out 
1
1
2
3
5
8
13
21
34
55

89
144
233
377
610

55
89
144
233
377

2.重载赋值操作符

什么时候需要重载赋值操作符?编译器是否提供默认的赋值操作

  • 编译器为每个类默认重载了赋值操作符
  • 默认的赋值操作符仅完成浅拷贝
  • 当需要进行深拷贝时必须重载赋值操作符
  • 赋值操作符拷贝构造函数有相同的存在意义

示例:

#include <iostream>
#include <string>

using namespace std;

class Test
{
    int* m_pointer;
public:
    Test()
    {
        m_pointer = NULL;
    }
    Test(int i)
    {
        m_pointer = new int(i);
    }
    Test(const Test& obj)
    {
        m_pointer = new int(*obj.m_pointer);
    }
    Test& operator = (const Test& obj)
    {
        if( this != &obj )
        {
            delete m_pointer;
            m_pointer = new int(*obj.m_pointer);
        }
        
        return *this;
    }
    void print()
    {
        cout << "m_pointer = " << hex << m_pointer << endl;
    }
    ~Test()
    {
        delete m_pointer;
    }
};

int main()
{
    Test t1 = 1;
    Test t2;
    
    t2 = t1;
    
    t1.print();
    t2.print();
    
    return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out 
m_pointer = 0x252f010
m_pointer = 0x252f030

(在C语言中支持自赋值,于是C++为了兼容C语言,也得支持自赋值。于是在重载赋值操作符的时候,也得处理自赋值的情况。)

问题分析:

一般性原则:
重载赋值操作符必然需要实现深拷贝!!!

编译器默认提供的函数:

3.string类

下面的代码输出什么?为什么?

示例:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "12345";
    const char* p = s.c_str();
        
    cout << p << endl;     
        
    s.append("abced");  // p 成为了野指针
        
    cout << p << endl;     

    
    return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out 
12345
12345

问题分析:

下面的程序输出什么?为什么?

示例:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    const char* p = "12345";
    string s = "";
    
    s.reserve(10);
    
    // 不要使用 C 语言中的方式操作 C++ 中的字符串
    for(int i=0; i<5; i++)
    {
        s[i] = p[i];
    }
    
    cout << s << endl;
    
    for(int i=0; i<5; i++)
    {
        cout << s[i] << endl;
    }
    
    return 0;
}

运行结果为空:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out 

1
2
3
4
5

问题分析:

改进后:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    const char* p = "12345";
    string s = "";
    
    s = p;
    
    cout << s << endl;
    
    return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out 
12345

4.小结

  • 函数调用操作符(( ))是可重载的
  • 函数调用操作符只能通过类的成员函数重载
  • 函数调用操作符可以定义不同参数的多个重载函数
  • 函数对象用于在工程中取代函数指针
  • 在需要进行深拷贝的时候必须重载赋值操作符
  • 赋值操作符拷贝构造函数有同等重要的意义
  • string类通过一个数据空间保存字符数据
  • string类通过一个成员变量保存当前字符串的长度
  • C++开发时尽量避开C语言中惯用的编程思想
posted @ 2018-12-07 22:35  PyLearn  阅读(379)  评论(0编辑  收藏  举报