分享
 
 
 

妖藏巨细(下)

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

妖藏巨细(下)

前言:可能是文章字数太长,我不知道为什么不能一起发表,所以分成上下两部分,这是第二部分。请读者耐心一点:)

3. 如何自由的调用重载的operator new和系统的operator new?

#include <iostream>

#include <vector>

#include <cstddef>

#include <new>

#include <memory>

using std::cout;

using std::endl;

using std::vector;

class memory

{

public:

memory() {}

~memory() { delete ptr; }

void *ptr;

std::size_t size;

std::size_t line;

};

vector<memory> v;

void* operator new(std::size_t size,std::size_t line=__LINE__)

throw (std::bad_alloc)

{

memory pm;

cout<<" "<<size<<endl;

pm.ptr=std::operator new(size); //希望调用系统的operator new,怎么用?

pm.size=size;

pm.line=line;

v.push_back(pm);

return pm.ptr;

}

void operator delete(void *ptr)

{

if (ptr == 0) return;

std::operator delete(ptr);

}

int main()

{

int* i=new int(3); //总是调用我写的operator new!

return 0;

}

将operator new、operator delete、array new或者array delete的标准global版本替换为自定义版本,这几乎从来都不是个好主意——即使C++标准允许你这么做。这些函数的标准版本一般都针对通用目的(general-purpose)的存储管理做了极大优化,而用户自定义的替代版本则不大会做得更好了。(然而,针对特定的类别或类别阶层体系采用(自定义的)成员函数形式的操作来定制其内存管理,则通常是合理的。)【注3】

注3:请参考Stephen C. Dewhurst写的《C++ Gotcha》Item62: Replacing Global New and Delete 。

这个程序有很多的毛病,我一一道来。

(1) 运算符(operator)应该全部在全局名字空间(global namespace)中(自定义时可以作为成员函数),也就是说+,-,*,/…都在global namespace中,operator new也是一个运算符。因此std::operator new(size);这种用法是错误的,应该换成::operator new(size);

(2) void* operator new(std::size_t size,std::size_t line=__LINE__) throw( bad_alloc )现在在global namespace中有两个operator new(自己写的一个,内建的一个)在这个函数中,有这样一句,pm.ptr=::operator new(size);你说现在的这个operator new是调用内建的那个,还是递归调用本身呢?你不知道,我不知道,编译器也不知道,因此编译时有歧义性!解决方法是让自己定义的operator new的第二个参数line不要有缺省引数(argument)。operator delete在这里直接调用内建的。

(3) memory类的设计有问题,析构函数调用delete,

{

memory pm;//定时炸弹开始计时

return pm.ptr;

}

//调用memory::~memory(),炸弹爆炸,伴随着魔鬼的狞笑。

解决办法是改写memory的析构函数为空!

最后这个程序如下:

// 前面不变

class memory

{

public:

memory() {}

~memory() { }//revised

void *ptr;

std::size_t size;

std::size_t line;

};

vector<memory> v;

void* operator new( std::size_t size,std::size_t line ) throw

(std::bad_alloc)//revised

{

memory pm;

cout<<" "<<size<<endl;

pm.ptr=::operator new(size); //调用系统的operator new

pm.size=size;

pm.line=line;

v.push_back(pm);

return pm.ptr;

}

void operator delete( void* p,std::size_t )

//match overloaded operator new

//应注意,当我们重载operator delete时,这些重载版本绝不会被“使用标准delete形式的表达式”唤起。

//在这个例子中,可以不重载operator delete,内建的已经够用了!后面就可以直接用delete i;不需要丑陋的operator delete(i,1);

{

::operator delete( p );//已经处理p=0

}

int main()

{

int* i= new(__LINE__) int(3); //调用我写的operator new,evised!

// delete i;//这种形式会调用global operator delete,当然在这里是完全可以的!

// i->~int();//不合法,但是如果是有nontrivial destructor的自定义类型,显示调用destructor不可避免!

operator delete(i,1);//调用自己重载的operator delete

return 0;

}

多么的丑陋,你真的需要自定义operator new and operator delete 吗?请三思而后行!!

4. 看看这个输出:cout<<(cout<<"HelloWorld"<<endl)<<endl;

Answer:<<是一个左移位运算符(shift operator),C++将其重载,变成输出流运算符,而且C++对各种基本类型都做了重载,因此我们能自然的使用<<输出int,float,double,char*,…,记住operator <<对void*也做了重载(后面会发现很重要!),所以如果你要用<<来输出自定义的类型,则必须自己重载operator <<。下面我们分两步来处理上面的问题;

(1)首先看(cout<<"HelloWorld"<<endl),输出HelloWorld,换行,刷新缓冲。在这里请记住operator <<的返回类型是std::ostream&;std::cout就是类型为std::ostream的一个对象。

(2)std::ostream是一个typedef。typedef basic_ostream<char, char_traits<char> > ostream;basic_ostream的父类是basic_ios;basic_ios有一个member function:operator void*(),转型运算符。现在已经很清楚!(cout<<"HelloWorld"<<endl)返回本身即:cout<<(cout)<<endl;(cout)调用operator void*()隐式转型为一个指针。然后在将类型为void*的指针输出,换行,刷新缓冲。(operator void*()的用途主要在于判断stream的状态是否失败,用在while(cout<<*p)等形式的连续输入中)。至于最终的指针值是多少,跟实现相关!

5. const A& A::operator=(const A& other)

{

if(this!=&other)

{

this->~A();

new(this) A(other);

}

return *this;

} 有错误吗?

Answer:这个问题的提出使我再次确认了一个事实,一句话不在于内容是什么,是否有意义,而在于说这句话的人是谁!hustli说出这句话,就会有人说他应该看看软件工程,不要炫耀奇技淫巧;但是如果是Herb Sutter说出来的,绝对不会有人要他去补软件工程的课,大部分人会认真的去看他提出的问题。(事实是Herb Sutter的确提出了类似的问题,并用pimpl手法解决的很漂亮!)

(1)const A&应该是A&,operator =应该返回一个可以改变的左值,因为我们可以这样用int a,b=2,c=4;(a=b)=c;所以我们应该也可以这样写:A a,b,c;(a=b)=c;不是吗?所以不能返回const reference。

(2)if(this!=&other)

{

this->~A();

new(this) A(other);

}

其实这一句很漂亮。就地析构,但不释放内存,然后用placement new就地定位构造对象。从技术上来看,它既不是必要的也不是充分的。在实践当中,它工作得颇好。可惜的是,有时候美丽只有一层皮厚!它不是异常安全的,异常安全有三种形式:

基本保证:元素还在,但可能状态改变!No resource leak!

强烈保证:要么成功,要么回到起点。绝对不会让你进退两难!

不抛出异常:一定成功,不会失败!

上面的这个操作连基本保证也没有达到!如果copy constructor失败,类的不变式(invariance)就可能被破坏!不过现在有一个比较好的解决方法,创建一个临时物,然后与*this交换。(copy and swap),自己创建一个成员函数swap(),并保证它的异常规范为throw(),使用pimpl手法是可以做到的。(可以参考www.gotw.ca中的Guru of the week)。

不过如果保证copy constructor成功的话,别的操作也正常。应该是不会有什么object slice出现的,即使有派生。因为this->~A()肯定是从叶子类(派生最远的类)的析构函数开始调用,一直到根类。【注4】。

注4:请参考ANSI C++标准12.4。

这个问题的答案还不够完整,为寻求彻底的解答,还需要再努力,在写这篇文章的时候我只能做到上面这些了。我最近发现Herb Sutter写的《Exceptional C++》中的Item 41. Object Lifetimes part 2有很详细的解答,大家可以去参考一下。

现在我们应该可以松一口气了。这5个问题的答案希望大家补充。

对初学者的一点小小的建议:

在我们这个浮萍的年代,只有浮萍的人生。真正能够沉下去认真学点东西,做点事情的人实在是凤毛麟角!因此首先要能够有一个正确的心态,欲速不达,千古不变的至理!有时候看到人家似乎学到很快,那是因为没有看到他以前投入的没入成本(sink cost),如果我们的基础好,一门语言学的很好,另外一门也会更快的入门。大家认真的学习过操作系统,编译原理,离散数学……吗?认真的做过一个程序吗?这条路我自己走的很难很难,现在最大的缺憾是一直就没有遇到一个能带给我巨大影响的师友,还有就是基础太差(专业跟计算机没有很大的关系)。

其次,我们也应该有宽容的心态。我们能够容忍不同的声音在耳边不断回响吗?当我们与别人争论的时候,能够在发表自己的观点之前,先在大脑中站在对方的位置想一想:为什么他会那样想,他为什么会有那样的观点。我想再说出自己的观点的时候,会更具有说服他人的能力!

最后,我想在学一门语言的时候,最开始应该学习基本的语法,以及标准库的使用,不要太早与魔鬼(细节)打交道,否则很容易进入魔道!并尽快的与实际项目,课题联系起来,没有课题自己也可以想,就是写写string、各种数据结构、基础算法也是非常有收获的。请记住,专业知识的学习非常重要,非常重要!语言有时候真的只是一门工具!学习编程有一个好办法,读程序,写程序。一定要写!

吴桐写于2003.4.26

最近修改2003.6.16

最近修改2003.6.15

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有