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语言中惯用的编程思想
C++解析(19):函数对象、关于赋值和string的疑问的更多相关文章
- 不可或缺 Windows Native (19) - C++: 对象的动态创建和释放, 对象的赋值和复制, 静态属性和静态函数, 类模板
[源码下载] 不可或缺 Windows Native (19) - C++: 对象的动态创建和释放, 对象的赋值和复制, 静态属性和静态函数, 类模板 作者:webabcd 介绍不可或缺 Window ...
- (63)Wangdao.com第十天_预处理、预解析_函数 上下文对象、参数列表对象
预解析.预处理 1. 在全局代码执行之前,js 引擎 就会创建一个栈来存储管理所有的 执行上下文对象 2. 在 全局执行上下文 window 确定以后,进行压栈 3. 在 函数执行上下文对象 确定以后 ...
- C++ 数组操作符重载、函数对象分析、赋值操作符
string类型访问单个字符 #include <iostream> #include <string> #include <sstream> using name ...
- 你不知道的JavaScript--Item6 var预解析与函数声明提升(hoist )
1.var 变量预编译 JavaScript 的语法和 C .Java.C# 类似,统称为 C 类语法.有过 C 或 Java 编程经验的同学应该对"先声明.后使用"的规则很熟悉, ...
- python 学习笔记3(循环方式;list初始化;循环对象/生成器/表推导;函数对象;异常处理)
### Python的强大很大一部分原因在于,它提供有很多已经写好的,可以现成用的对象 16. 循环方式笔记: 1)range(0, 8, 2) #(上限,下限,步长) 可以实现对元素或者下标的 ...
- python之三元表达式,列表|字典推导式,函数对象
#### 三元表达式: 就是if....else...的语法糖 # -- 1) 只能解决if...else...结构,其他if分支结构都不管 # -- 2)一个分支提供一个结果: 如果一个分支提供了多 ...
- Atitit dsl对于数组的处理以及main函数的参数赋值
Atitit dsl对于数组的处理以及main函数的参数赋值 1.1. 词法解析..添加了[] 方括号的解析支持1 1.2. Ast建立.添加了数组参数的支持..使用了递归下降法..getparam ...
- Python进阶07 函数对象
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 秉承着一切皆对象的理念,我们再次回头来看函数(function).函数也是一个对象 ...
- python 函数对象(函数式编程 lambda、map、filter、reduce)、闭包(closure)
1.函数对象 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 秉承着一切皆对象的理念,我们再次回头来看函数(function).函 ...
随机推荐
- [BZOJ4002][JLOI2015]有意义的字符串-[快速乘法+矩阵乘法]
Description 传送门 Solution 由于这里带了小数,直接计算显然会爆掉,我们要想办法去掉小数. 而由于原题给了暗示:b2<=d<=(b+1)2,我们猜测可以利用$(\fra ...
- 【转载】CPU阿甘
原文:CPU阿甘 前言 上帝为你关闭了一扇门,就一定会为你打开一扇窗这句话来形容我最合适不过了.我是CPU, 他们都叫我阿甘, 因为我和<阿甘正传>里的阿甘一样, 有点傻里傻气的.上帝 ...
- 利用Anaconda进行python爬虫环境的配置-安装scrapy
1.下载Anaconda,下载地址:https://www.continuum.io/downloads 2.安装anaconda. 3.安装scrapy
- 探究linux设备驱动模型之——platform虚拟总线(一)
说在前面的话 : 设备驱动模型系列的文章主要依据的内核版本是2.6.32的,因为我装的Linux系统差不多就是这个版本的(实际上我用的fedora 14的内核版本是2.6.35.13的.) ...
- katalon系列八:Katalon Studio图片识别
Katalon Studio自带集成了图片识别功能,有2个比较有用的图片识别相关的命令:Wait For Image Present和Click Image.这里重点讲下Click Image命令: ...
- Lua学习笔记(3):运算符
算术运算符 运算符 描述 + 加法运算符 - 减法运算符 * 乘法运算符 / 除法运算符 % 取模运算符 ^ 乘幂 A=3 print(A^2)输出9 关系运算符 ~= 不等于 == 等于 > ...
- Zookeeper--java操作zookeeper
如果是使用java操作zookeeper,zookeeper的javaclient 使我们更轻松的去对zookeeper进行各种操作,我们引入zookeeper-3.4.5.jar 和 zkclien ...
- Python登录,输入三次密码
第一段python代码,写了一天,总算不报错了,值得纪念. 基本要求: 写一个登录界面,登录三次锁定用户 1. 包含一个用户信息文件,用户名和密码 2.黑名单文件 过程: 1.先检查是否在黑名单中,如 ...
- 解决xampp启动mysql失败
进入到注册表内 命令:regedit 进入到路径:计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\MySQL 修改路径为:" ...
- python编辑购物车
一.需求分析 输入工资金额,进入购物车,并打印输出商品编号和价格,用户可以通过输入商品编号进行商品选购 余额不足时,打印提示信息 通过q进行退出结算 购物车能够循环购物 二.代码实现 ShoopCar ...