??xml version="1.0" encoding="utf-8" standalone="yes"?> 在新颁布的C++新标准C++11中,最令hȀ动h?j)的Q我想不是auto关键字,也不是Lambda表达式,而是其中的对q行计算的支持——新的线E库(thread)的加入?/p> 多核?j)CPU的普?qing)应用,C++的主要应用领域,服务器程序,高性能计算{等Q都对ƈ行计提Z(jin)新的要求Q而这ơC++中全新添加的U程库,是
对这一势的应寏V现在,C++E序员可以轻村֜~写多线E的E序Q而无需借助pȝAPI或者是W三方程序库的支持。线E库的加入给C(j)++带来的变化,?
异于 194Q翻w的E序员们把歌唱?/p> C++11中的U程库,很大E度上直接来自boostq块C++的试验田Q其基本架构和组仉完全相同Q如果你是一个boostU程库的使用者,?
么在C++11中,你会(x)感觉到是回到?jin)老家一P到处都是熟h。而如果你是一个完全的新手Q也不要紧,C++11中的U程库非常简单,M人都可以L?
手,我就是这P但是要深IӞq得好好学习(fn)?/p> 下面是一个简单的例子Q用C(jin)U程库中的线E(threadQ?互斥QmutexQ?条g变量QconditionQ,来模拟一个演׃(x)的入场检的场景Q另外,Z(jin)模拟观众Q用C(jin)C++11中的新的随机数的产生Q模拟一个正态分布的访客人群。不说了(jin)Q还是看代码Q?/p> #include <iostream> // ׃n资源和互斥对?br />mutex mtx; // 同样q用条g变量Q判断队列是否已l满?br /> // 只有在队列尚未满的情况下才向下l?br /> scoped_lock lock(m); 在C++11中,我们可以使用shared_ptr理某个对象的所有权Q负责对象的析构。然而在某些情况下,我们只是希望安全的访问某个对象,而不x有这个对象的所有权Q对q个的析构负责(有点像电(sh)视剧中的那些不负责Q的男人哦Q只是玩玩而已Q不?x)负责?j)。在q种情况下,我们可以使用表示弱引用的weak_ptr?/p>
weak_ptr可以׃个shared_ptr构徏Q表C个weak_ptr拥有q个shared_ptr所指向的对象的讉K权,注意Q这里仅仅是讉K权,它不?x)改变智能指针的引用计数Q自然也׃?x)去析构q个对象。利用weak_ptrQ我们就可以安全地访问那些不具备所有权的对象?/p>
一个现实中的例子就是学校的传达室,传达室拥有一本学生的名单Q如果某个电(sh)话来?jin)找某个学生Q传辑֮?x)根据花名册d试访问这个学生,如果q个学生q在学校Q就直接呼叫q个学生Q如果已l离开?jin),q给q个学生留一个消息。在q里Q花名册上的学生可能q在学校Q对象还存在Q,也可能已l离开学校Q对象已l析构)(j)Q我们都需要对其进行访问,而weak_ptr是用来讉Kq种不确定是否存在的对象的?/p>
]]>
]]>
#include <queue>
#include <vector>
// 随机?br />#include <random>
// q里Q我使用?jin)boost实现的线E库Q如果你的编译器已经支持C++11Q则使用<thread>是一L(fng)
#include <boost\thread.hpp>
#include <boost\thread\locks.hpp>
#include <boost\thread\condition.hpp>
using namespace std;
using namespace boost;
bool finish = false; // 表示观众到来是否l束
// 观众Q主要是Z(jin)表示(g)过E中的检耗费旉
class viewer
{
public:
void check()
{
// U程{待
posix_time::milliseconds worktime(400);
this_thread::sleep(worktime);
}
void arrival(int t)
{
posix_time::seconds arrtime(t);
this_thread::sleep(arrtime);
}
};
// (g)口
// 它有一个队列,用于保存到来的观众,q且用一个线E来处理队列中的观众
class gate
{
typedef boost::mutex::scoped_lock scoped_lock;
public:
gate():count(0),no(0){};
// 启动U程
void start(int n)
{
no = n;
t = thread(&gate::check,this);
}
// (g)?br /> void check()
{
// 无限循环Q知道观众数?且不?x)有新的观众到?br /> while(true)
{
viewer v;
{
// 锁定互斥对象Q开始访问对?br /> scoped_lock lock(m);
if(0==vque.size()) // 如果队列为空
{
{
// 判断是否q会(x)有新的观众到来,也即是表C到辄U程是否l束
scoped_lock finlk(mtx);
if(finish)
return; // 如果已经l束Q检也同样l束
}
// 如果观众Cؓ(f)0Q则{待新的观众的到?br /> while(0 == vque.size())
{
// q里的wait()是条件变量的关键Q它?x)先是否lock所锁定的互斥对象m一定时_(d)
// 然后再次锁定Q接着q行Q?==vque.size()Q的判断。如此往复,知道size不等?Q?br /> // 循环条g无法满而结束@环,q里表达的条件就是,只有size!=0Q也是队列中有
// 观众才l向下?
cond.wait(lock);
}
}
// 从对列中获得观众Q对其进行检?
v = vque.front();
vque.pop();
cond.notify_one(); // q里是通知d观众的进E,表示队列已经有空位置?jin),可以d新的观众
}
v.check();
++count;
}
}
// 观众添加到队列
void add(viewer v)
{
while(vque.size() >= 15 )
{
cond.wait(lock);
}
vque.push(v); // 观众添加到队列
cond.notify_one(); // 通知(g)进E,新的观众q入队列Q这样在size=0时等待的条g可以更新
}
int getcount()
{
return count;
}
int getno()
{
return no;
}
// {待U程执行完毕q回
void join()
{
t.join();
}
private:
thread t;
mutex m;
condition cond;
queue<viewer> vque;
int count;
int no;
};
// 一共有10个检口
vector<gate> vgates(10);
// 用随机数模拟观众到达
void arrival()
{
default_random_engine re{}; // 产生一个均gؓ(f)31的正态分布的随机?br /> normal_distribution<double> nd(31,8);
// 随机数引擎和分布绑定一个函数对?br /> auto norm = std::bind(nd, re);
// 保存随机数的容器
vector<int> mn(64);
// 产生随机?br /> for(int i = 0;i<700;++i)
++mn[round(norm())];
int secs = 100;
// 产生0?的随机数Q表C众随机地到达某一个检口
uniform_int_distribution<int> index{0,9};
// q入(g)口队列
for(auto i:mn)
{
cout<<i<<endl;
for(auto vi = 1; vi <= i; ++vi)
{
// 观众添加到某个gate的队列中
(vgates[index(re)]).add(viewer());
// {待一D|?br /> int t = round(secs/(float)(i+1));
this_thread::sleep(
posix_time::milliseconds(t));
}
}
// 观众已经全部到达Q进入队?br /> cout<<"finish"<<endl;
mtx.lock();
finish = true;
mtx.unlock();
//cout<<"unlock"<<endl;
}
int main()
{
int i = 1;
// 启动(g)线E?br /> for(gate& g:vgates)
{
g.start(i);
++i;
}
// 启动到达U程Q看看,在C++11中新U程的创建就q么?br /> thread arr = thread(arrival);
// {待U程l束
arr.join();
int total = 0;
// {待(g)线E结束,q输出处理的人数
for(gate& g:vgates)
{
g.join();
total += g.getcount();
cout<<"gate "<<g.getno()
<<" processed "<<g.getcount()<<" viewers."<<endl;
}
cout<<"there are "<<total<<"viewers in total."<<endl;
return 0;
}
q就是一个线E库的简单应用,模拟?jin)非常复杂的场景?/p>
因ؓ(f)自己对多U程开发还不太熟?zhn)Q这个程序在某些特定条g下会(x)产生?jin)死锁,q有待进一步完?iostream>
]]>
http://imcc.blogbus.com/
C++品Q吃火锅与shared_ptrQ指针,拯构造函数和delete读者Terry问到一个关于拷贝构造函数的问题Q大家可以参?a >{TerryQ拷贝构造函?/a>Q其中论qC(jin)拯构造函数的必要性,然而,M事物都是h两面性的Q有时候我们需要自己定义类的拷贝构造函数来完成cȝ拯Q然后,有的时候,q种以拷贝一个对象来完成另外一个对象的创徏是不合理的(也就是在现实世界中,q种对象没有可复制性,例如Qh民币Q,是应该被止的。我们来举一个吃火锅的例子:(x)
// 火锅Q可以从中取出其中烫的东?
class hotpot
{
public:
hotpot(string f) :
food(f)
{
}
string fetch()
{
return
food;
}
private:
string food;
};
// 吃火锅用的碗Q当然是每个Z用的
class bowl
{
public:
bowl(string o) :
owner(o)
{
}
void put(string food)
{
cout<<"put
"< }
private:
string owner;
};
// 吃火锅的?br />class
human
{
public:
// 名子和吃的火?
human(string n,shared_ptr ppot) :
name(n),pot(ppot)
{
pbowl = new bowl(name);
};
//
OK?jin),从火锅中取出来放到自q里
void fetch()
{
string food =
pot->fetch();
// 攑ֈ自己的碗?
coutput(food);
}
private:
string name;
shared_ptr pot;
bowl* pbowl;
};
int
main()
{
// 服务员端上来牛肉火锅
shared_ptr fpot(new hotpot("beaf"));
//
terry入席
human terry("terry",fpot);
//
又来?jin)一个姓陈的Q这里用的是默认的拷贝构造函数来创徏terry的副?br />human chen = terry;
//
terry夹了(jin)一块肉
terry.fetch();
// 陈先生也夹了(jin)一块肉
chen.fetch();
return 0;
}
到这里,g看v来一切OKQ然而从E序输出中我们却发现?jin)问题?x)
terry put beaf into terry's bowl.
terry put beaf into terry's bowl.
O my godQ明明是两个人(terry和chenQ,但是好像却只有一个h做了(jin)两次Q陈先生也把肉加C(jin)terry的碗里?/p>
q就是当cM有指针类型的数据成员Ӟ使用默认的拷贝构造函数所带来的问题,D其中的某些指针成员没有被合理地初始化Q这别是当这些指针指向的是与q个对象QhumanQ有所属关pȝ资源QbowlQ,在这U时候,我们必须自己定义cȝ拯构造函敎ͼ完成指针成员的合理初始化。在human中添加一个拷贝构造函?/p>
human(const human& h)
{
// 两个人显然不能同名,所以只好给个无名氏?
name =
"unknown";
// 使用不同的碗
// bowl和human有所属关p,所以这里必d建新的对?
pbowl = new
bowl(name);
// 不过可以吃同一个火?
// pot和humanq没有所属关p,所以可以共享一个对?br />pot =
h.pot;
};
d拯构造构造函C后,两个Z?x)将东西攑ֈ同一个碗中了(jin)Q自己取得东西不?x)放到别人的里Q?/p>
terry put beaf into terry's bowl.
unknown put beaf into unknown's
bowl.
q样修改好多?jin),臛_两个Z?x)用同一个碗?jin)。然而,q样q是有问题,我们无法l第二个人命名,他成?jin)无名氏了(jin),q就是类当中的那些没有可复制性的数据成员Q一个h的名字自然不可以复制l另外一个hQ如果human中有个wifeQ那肯定要上演世界大战了(jin)Q,拯构造函数就?x)生这L(fng)问题?/p>
实际上,对于q类不具备可复制性的对象Qؓ(f)?jin)不引v混ؕQ其拯构造操作是应当被禁止的Q新标准C++11注意到?jin)这个问题,提供了(jin)一个delete关键字来用某些可能存在的(即你规定human不可复制Q也无法LE序员在使用human时写出human chen = terryq样的不合理的代码)(j)默认的(cȝ拯构造函数是默认提供的,对于那些不具备可复制性的cL_(d)q简直是画蛇添Q好?j)办了(jin)坏事情Q不合理的操作,q样Q我们就不能使用拯 构造函C(jin)Q?/p>
// 用human的拷贝构造函?br />human(const human& h) = delete;
l过q样的定义,当我们在代码中尝试将一个对象复制给另外一个对象(?x)调用拷贝构造函敎ͼ(j)Ӟ~译器就?x)出错误提示Q提醒程序员QhiQ这样可不行Q我是独一无二的,不能够被复制
human chen = terry;
~译器给q样的提C:(x)
Noname1.cpp:41:2: error: deleted function 'human::human(const
human&)'
Noname1.cpp:59:15: error: used here
所以,ȝhQ在使用拯构造函数时Q有两个需要注意的地方Q?/p>
5.1.2 函数调用机制
在学?fn)编写函C前,我们首先要了(jin)解函数的调用机制Q学?x)如何调用一个已l存在的函数。世界上已经有很多函敎ͼ我们可以直接调用q些函数来完成日怓Q务。世界上已经有很多轮子,我们没有必要再去发明更多同样的轮子,只需要用好它们就可以?jin)。在实际的开发中Q可供调用的现有函数主要有编译器提供的库函数、Windows API?qing)第三方提供的函数库{。通过调用他h的函敎ͼ可以复用他h的开发成果,在其开发成果的基础上,实现快速开发,如图5-3所C?/p>
有了(jin)别h提供的函敎ͼ可以调用这些函数来完成自己的功能。两个函C间的关系是调用与被调用的关系Q我们把调用其他函数的函数称Z调函敎ͼ被其他函数调用的函数UCؓ(f)被调函数。一个函数是主调函数q是被调函数q不是绝对的Q要Ҏ(gu)其所处的相对位置而定Q如果一个函数内部有函数Q则相对其内部的函数它就是主调函敎ͼ如果它的外部有函敎ͼ则相对其外部函数它就是被调函数?/p>
?-3 天上掉下个函数库
5.1.1 程序装到箱子中Q函数的声明和定?/p>
提问Q把大象装到冰箱中需要几步?
回答Q需要三步。第一Q打开冰箱Q第二,把大象放q冰;W三Q关上冰?/p>
提问Q那么,把一个程序放q箱子需要几步?
回答Q需要两步。第一Q声明一个函敎ͼW二Q定义这个函数?/p>
没错Q把一个函数放q箱子比把大象放q冰p要简单。当分析一D长的程序代码时Q往往?x)发C些代码所实现的功能相Ҏ(gu)较独立。我们将E序中这些相Ҏ(gu)较独立的功能代码l织CP用函数对其进行封装,也就是将一个较长的E序分放到各个函数箱子中?/p>
要装东西Q先得准备好子。ؓ(f)?jin)找到具体功能实C码的子Q需要给子贴上标签Q这个标{ְ是函数的声明Q如?-2所C?/p>
?-2 声明一个函敎ͼ为箱子脓(chung)?/p>
t 把程序装q箱子:(x)用函数封装程序功?a >
在完成豪华的工资l计E序之后Q我们信?j)倍增Q开始向C++世界的更p处探索?/p>
现在Q可以用各U数据类型和E序程控制l构来编写完整的E序?jin)。但是,随着要处理的问题来复杂,E序的代码也来复杂,dC来长?jin)。这像我们所有东襉K堆放C个仓库中Q随着东西来多Q仓库慢慢就被各U东西堆满了(jin)Q显得杂乱无章,理h非常困难。面对一个杂乱无章的仓库Q聪明的仓库理员提供了(jin)一个很好的理办法Q将东西分门别类地装q箱子,然后有序地堆攑个箱子?/p>
q个好方法也可以用到E序设计中,把程序装q箱子,让整个程序结构清晰?/p>
5.1 函数是一个大子
当要处理的问题越来越复杂Q程序越来越庞大的时候,如果把这些程序代码都攑ֈdCQ将使得整个d数异常臃肿,q样?x)给E序的维护带来麻?ch)。同Ӟ要让一个主函数来完成所有的事情Q几乎是一个不可能完成的Q务。在q种情况下,可以Ҏ(gu)“分而治之”的原则Q按照功能的不同大的程序进行模块划分,h相同功能的划分到同一个模块中Q然后分别处理各个模块。函敎ͼ则成为模块划分的基本单位Q是对一个小型问题处理过E的一U抽象。这像理一个仓库,L同cȝ东西攑ֈ同一个箱子中Q然后通过理q些子来管理整个仓库。在具体的开发实践中Q我们先相对独立的、经怋用的功能抽象为函敎ͼ然后通过q些函数的组合来完成一个比较大的功能。D一个简单的例子Q看书看得肚子饿?jin),我们要?chng)方便面吃。这其实是一个很复杂的过E,因ؓ(f)q一q程中我们先要洗锅,然后烧水Q水烧开后再泡面Q吃完面后还要洗。如果把整个q程描述在主函数中,那么dC(x)非常复杂Q结构乱。这时就可以使用函数来封装整个过E中的一些小步骤Q让整个d数简化ؓ(f)对这些函数的调用Q如?-1所C?/p>
?-1 程序封装到子Q分而治?/p>
4.3.4 对@环进行控Ӟ(x)break与continue
// 大款的收支统计程?/p>
int nTotal = 0;
int nInput = 0;
do
{
cout<< "误入你的收入或支出Q?;
cin>>nInput;
if( 1000< nInput ) // 毛毛雨啊Q就不用l计?/p>
continue;
nTotal += nInput;
}while( 0 != nInput );
在这个大Ƅ收支l计E序中,nInput接收用户输入后判断其值是否小? 000Q如果小? 000Q则执行continue关键字,跌后面的加和语句“nTotal += nInput;”,而直接蟩转到Ҏ(gu)件表辑ּ? != nInput”的计算Q判断是否可以开始下一ơ@环。值得注意的是Q在for循环中,执行continue后,控制条g变化的更改语句ƈ没有被蟩q,仍然被执行Q然后再计算条g表达式,试下一ơ@环?/p>
虽然break和continue都是在某U条件下跛_循环Q但是两者有本质的差别:(x)break是蟩出整个@环,立刻l束循环语句的执行;而continue只蟩出本ơ@环,l箋执行下一ơ@环。图4-6展示?jin)break和continue之间的区别?/p>
?-6 break和continue之间的区?/p>
The world is built on C++.
—?/em> Herb Sutter
看得有趣、学得轻?/p>
看图也能学C++Q!
没错Q看图也能学C++Q?/p>
q本q你书是《我的第一本C++书》的q你版,它抽取了(jin)《我的第一本C++书》中的全部的_插图Qƈ配上相应的解释说明。它以图文ƈ茂的生动形式Q向你讲解那些所谓的高深的C++知识Q让你对那些抽象的C++知识有一个更加Ş象的理解Q向你展C个美丽而神U的C++世界Q让你在有趣的看图过E中Q轻村֜学到?jin)C++知识?/p>
看得有趣、学得轻?