C++_虚构造函数和虚析构函数

BCB支持C++标准:构造函数不支持多态.也就是说,C++的构造函数不能是虚的,在构造函数中调 
用虚函数也会被当作普通函数去静态连接. 
不过,BCB为了支持VCL,增加了delphiclass类型的类(__declspec(delphiclass)),这种类的构 
造函数支持多态.    无谓空间
你让你的基类从TObject继承就可以了: 
#inlcude  <system.hpp> 
class  TMyBase:  TObject 
public: 
virtual  TMyBase(char  *  AName); 
__fastcall  ~TMyBase();//析构函数必须定义成__fastcall,因为在TObject中把析构函数定义成了这种类型。 
};
/////////////////////////////////////////
(C++)虚构造函数和虚析构函数
1,没有虚构造函数,如果有,会给对象实例化带来问题,但是可以模拟虚拟构造函数的效果。
一种允许你做一些C++不直接支持的事情的用法。
你可能通过虚函数virtual clone()(对于拷贝构造函数)或虚函数virtual create()(对于默认构造函数),得到虚构造函数产生的效果。
Class Shape {
public:
virtual ~Shape(){}//虚析构函数
virtual void draw()=0; //纯虚函数
virtual void move()=0; 
// ... 
virtual Shape* clone() const=0; //使用拷贝构造函数
virtual Shape* create() const=0; //使用默认构造函数
};
Class Circle: public Shape{
public:
Circle* clone() const{ return new Circle(*this); }
Circle* create() const{ return new Circle(); }
// ... 
};
在clone()成员函数中,代码new Circle(*this)调用Circle的拷贝构造函数来复制this的状态到新创建的Circle对象。在create()成员函数中,代码new Circle()调用Circle的默认构造函数。
统计与决策
用户将它们看作“虚构造函数”来使用它们:
Void userCode(Shape& s)
{
Shape* s2=s.clone();
Shape* ate();
//  ... 
delete s2; //在此处,你可能需要虚析构函数
delete s3;
}
这个函数将正确工作,而不管Shape是一个Circle,Square,或是其他种类的Shape,甚至它们还并不存在。
注意:成员函数Circle's clone()的返回值类型故意与成员函数Shape's clone()的不同。这种特征被称为“协变的返回类型”,该特征最初并不是语言的一部分。如果你的编译器不允许在Circle类中这样声明Circle* clone() const(如,提示“The return type is different”或“The member function's type differs from the base class virtual function by return type clone”),说明你的编译器陈旧了,那么你必须改变返回类型为Shape*。 
2、可以有虚析构函数,主要用于用一个基类的指针删除一个派生类的对象时。
class ClxBase{
public:
ClxBase(){cout << "Output from the con ClxBase!" << endl;};
virtual ~ClxBase(){cout << "Output from the destructor of class ClxBase!" << endl;};
virtual void DoSomet
hing(){ cout << "Do something in class ClxBase!" << endl; };
};
class ClxDerived : public ClxBase{
public:
ClxDerived() {cout << "Output from the con ClxDerived!" << endl;};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};
void main()
{
ClxBase *pTest = new ClxDerived;
pTest->DoSomething();
delete pTest;
}
的输出结果是:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
这个很简单,非常好理解。
但是,如果把类ClxBase析构函数前的virtual去掉,那输出结果就是下面的样子了:
Do something in class ClxDerived!
也就是说,类ClxDerived的析构函数根本没有被调用!一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。我想所有的C++程序员都知道这样的危险性。当然,如果在析构函数中做了其他工作的话,那你的所有努力也都是白费力气。
当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。
3、定义纯虚析构函数(pure virtual destructor)
纯虚成员函数通常没有定义;它们是在抽象类中声明,然后在派生类中实现。比如说下面的例子:
class File //an abstract class
{
public:
virtual int open(const string & path, int mode=0x666)=0;
virtual int close()=0;
//...
};
但是,在某些情况下,我们却需要定义一个纯虚成员函数,而不仅仅是声明它。最常见的例子是纯虚析构函数。在声明纯虚析构函数时,不要忘了同时还要定义它。
class File //abstract class
{
public:
virtual ~File()=0; //declaration of a pure virtual dtor
};
File::~File() {} //definition of dtor
halin
为什么说定义纯虚析构函数是非常重要的。派生类的析构函数会自动调用其基类的析构函数。这个过程是递归的,最终,抽象类的纯虚析构函数也会被调用。
如果纯虚析构函数只被声明而没有定义,那么就会造成运行时(runtime)崩溃。(在很多情况下,这个错误会出现在编译期,但谁也不担保一定会是这样。)纯虚析构函数的哑元实现(dummy implementation,即空实现)能够保证这样的代码的安全性。
class DiskFile : public File
{
public:
int open(const string & pathname, int mode);
int close();
~DiskFile();
};
File * pf = new DiskFile;
//. . .
delete pf; //OK, ultimately invokes File::~File()
在某些情况下定义
其它纯虚成员函数可能也是非常有用的(比如说在调试应用程序以及记录应用程序的日志时)。例如,在一个不应该被调用,但是由于一个缺陷而被调用的基类中,如果有一个纯虚成员函数,那么我们可以为它提供一个定义。
class Abstract
{
public:
virtual int func()=0;
//…
};
int Abstract::func()
{
std::cerr<<"got called from thread " << thread_id<<
"at: "<<gettimeofday()<<std::endl;
}
这样,我们就可以记录所有对纯虚函数的调用,并且还可以定位错误代码;不为纯虚函数提供定义将会导致整个程序无条件地终止。
/////////////////////////////////////////////
模拟虚拟构造函数
在C++面向对象程序设计中,我们都知道析构函数是可以虚拟的,但构造函数确是不能够虚拟的。原因在于虚拟调用是一种能够在给定信息不完全的情况下工作的机制。特别地,虚拟允许我们调用某个函数,对于这个函数,仅仅知道它的接口,而不知道具体的对 象类型。但是要建立一个对象,你必须拥有完全的信息。特别地,你需要知道要建立的对象的具体类型。因此,对构造函数的调用不可能是虚拟的。
  但我们可以模拟虚拟构造函数。
混凝土裂缝论文class Base
{
public:
Base(string key):m_key(key){}
string GetKey(){
return m_key;
}
virtual int GetValue()=0;
private:
string m_key;
};
class A:public Base
{
public:
A(string key):Base(key){}
int GetValue(){
return 1;
}
};
class B:public Base
{
public:
B(string key):Base(key){}
int GetValue(){
return 2;
}
};
class C:public Base
{
public:
C(string key):Base(key){}
int GetValue(){
return 3;
}
};
class Wrap
{
public:
Wrap(string key){
if(key=="a") m_obj= new A(key);
if(key=="b") m_obj= new B(key);
if(key=="c") m_obj=new C(key);
}
~Wrap(){
delete m_obj;
}
Base *GetObj(){
return m_obj;
}
private:
Base *m_obj;
};
这样的设计方案也许会对我们的程序设计有所帮助
/////////////////////////////////////////////////
虚拟构造函数的用处
2008-06-11 15:13:59 来源:中国自学编程网 作者:佚名 点击:156
--------------------------------------------------------------------------------
从字面来看,谈论“虚拟构造函数”没有意义。当有一个指针或引用,但是不知道其指向对象的真实类型是什么时,可以调用虚拟函数来完成特定类型(type-specific)对象的行为。
从字面来看,谈论“虚拟构造函数”没有意义。当有一个指针或引用,但是不知道其指向对象的真实类型是什么时,可以调用虚拟函数来完成特定类型(type-specific)对
象的行为。仅当还没拥有一个对象但是又确切地知道想要的对象的类型时,才会调用构造函数。那么虚拟构造函数又从何谈起呢?
很简单。尽管虚拟构造函数看起来好像没有意义,其实它们有非常大的用处.例如,假设编写一个程序,用来进行新闻报道的工作,每一条新闻报道都由文字或图片组成。可以这样管理它们:
class NLComponent {              //用于 newsletter components 
public:                          // 的抽象基类 
...                            //包含至少一个纯虚函数
};
class TextBlock: public NLComponent {
public:
.
..                            // 不包含纯虚函数
}; 
class Graphic: public NLComponent {
public:
...                            // 不包含纯虚函数
};
class NewsLetter {                // 一个 newsletter 对象
public:                          // 由NLComponent 对象
...                            // 的链表组成
private:
list<NLComponent*> components;
};
在NewsLetter中使用的list类是一个标准模板类(STL)。list类型对象的行为特性有些象双向链表,尽管它没有以这种方法来实现。对象NewLetter不运行时就会存储在磁盘上。为了能够通过位于磁盘的替代物来建立Newsletter对象,让NewLetter的构造函数带有istream参数是一种很方便的方法。当构造函数需要一些核心的数据结构时,它就从流中读取信息:
class NewsLetter {
public:
NewsLetter(istream& str);
...
};
此构造函数的伪代码是这样的:
NewsLetter::NewsLetter(istream& str)
小说村子
{
while (str) { [Page]
从str读取下一个component对象; 
把对象加入到newsletter的 components对象的链表中去;
}
}
或者,把这种技巧用于另一个独立出来的函数叫做readComponent,如下所示:
class NewsLetter {
public:
... 
private:
/
/ 为建立下一个NLComponent对象从str读取数据,
// 建立component 并返回一个指针。
static NLComponent * readComponent(istream& str);
...
};
NewsLetter::NewsLetter(istream& str)
{
while (str) {
// 把readComponent返回的指针添加到components链表的最后,
// \"push_back\" 一个链表的成员函数,用来在链表最后进行插入操作。
components.push_back(readComponent(str));
}
}
考虑一下readComponent所做的工作。它根据所读取的数据建立了一个新对象,或是TextBlock或是Graphic。因为它能建立新对象,它的行为与构造函数相似,而且因为它能建立不同类型的对象,我们称它为虚拟构造函数。虚拟构造函数是指能够根据输入给它的数据的不同而建立不同类型的对象。虚拟构造函数在很多场合下都有用处,从磁盘(或者通过网络连接,或者从磁带机上
)读取对象信息只是其中的一个应用。
还有一种特殊种类的虚拟构造函数――虚拟拷贝构造函数――也有着广泛的用途。虚拟拷贝构造函数能返回一个指针,指向调用该函数的对象的新拷贝。因为这种行为特性,虚拟拷贝构造函数的名字一般都是copySelf,cloneSelf或者是象下面这样就叫做clone。很少会有函数能以这么直接的方式实现它:
class NLComponent {
public:
// declaration of virtual copy constructor
virtual NLComponent * clone() const = 0;
... 
}; 
class TextBlock: public NLComponent {
public:
virtual TextBlock * clone() const        // virtual copy
{ return new TextBlock(*this); }          // constructor
... 
}; 
class Graphic: public NLComponent {
what will be will be
public:
virtual Graphic * clone() const            // virtual copy
{ return new Graphic(*this); }            // constructor [Page]
... 
};
正如我们看到的,类的虚拟拷贝构造函数只是调用它们真正的拷贝构造函数。因此“拷贝”的含义与真正的拷贝构造函数相同。如果真正的拷贝构造函数只做了简单的拷贝,那么虚拟拷贝构造函数也做简单的拷贝。如果真正的拷贝构造函数做了全面的拷贝,那么虚拟拷贝构造函数也做全面的拷贝。如果真正的拷贝构造函数做一些奇特的事情,象引用计数或copy-on-write,那么虚拟构造函数也这么做。
注意上述代码的实现利用了最近才被采纳的较宽松的虚拟函数返回值类型规则。被派生类重定义的虚拟函数不用必须与基类的虚拟函数具有一样的返回类型。如果函数的返回类型是一个指向基类的指针(或一个引用),那么派生类的函数可以返回一个指向基类的派生类的指针(或引用)。这不是C++的类型检查上的漏洞,它使得有可能声明象虚拟构造函数这样的函数。这就是为什么TextBlock的clone函数能够返回TextBlock*和Graphic的clone能够返回Graphic*的原因,即使NLComponent的clone返回值类型为NLComponent*。
在NLComponent中的虚拟拷贝构造函数能让实现NewLetter的(正常的)拷贝构造函数变得很容易:
class NewsLetter {
public:
NewsLetter(const NewsLetter& rhs);
... 
private:
list<NLComponent*> components;
}; 
NewsLetter::NewsLetter(const NewsLetter& rhs)
{
// 遍历整个rhs链表,使用每个元素的虚拟拷贝构造函数
/
/ 把元素拷贝进这个对象的component链表。
for (list<NLComponent*>::const_iterator it =
rhsponents.begin();
it != d();
++it) { 
// \"it\" 指向rhsponents的当前元素,调用元素的clone函数,
// 得到该元素的一个拷贝,并把该拷贝放到
// 这个对象的component链表的尾端。
components.push_back((*it)->clone());
}
}
历被拷贝的NewsLetter对象中的整个component链表,调用链表内每个元素对象的虚拟构造函数。我们在这里需要一个虚拟构造函数,因为链表中包含指向NLComponent对象的指针,但是我们知道其实每一个指针不是指向TextBlock对象就是指向Graphic对象。无论它指向谁,我们都想进行正确的拷贝操作,虚拟构造函数能够为我们做到这点。
虚拟化非成员函数
就象构造函数不能真的成为虚拟函数一样,非成员函数也不能成为真正的虚拟函数。然而,既然一个函数能够构造出不同类型的新对象是可以理解的,那么同样也存在这样的非成员函数,可以根据参数的不同动态类型而其行为特性也不同。例如,假设你想为TextBlock和Graphic对象实现一个输出操作符。显而易见的方法是虚拟化这个输出操作符。但是输出操作符是operator<<,函数把ostream&做为它的左参数(left-hand argument)(即把它放在函数参数列表的左边),这就不可能使该函数成为TextBlock 或 Graphic成员函数。 [Page]
(这样做也可以,不过看一看会发生什么:
class NLComponent {
public:
// 对输出操作符的不寻常的声明
virtual ostream& operator<<(ostream& str) const = 0;
...
}; 
class TextBlock: public NLComponent {
public:
// 虚拟输出操作符(同样不寻常)
virtual ostream& operator<<(ostream& str) const;
}; 
class Graphic: public NLComponent {
public:
// 虚拟输出操作符 (不寻常)
virtual ostream& operator<<(ostream& str) const;
};
TextBlock t;
Graphic g; 
... 
t << cout;                                  // 通过virtual operator<<
//把t打印到cout中。
// 不寻常的语法
g << cout;                                  //通过virtual operator<<
把g打印到cout中。
//不寻常的语法
类的使用者得把stream对象放到<<;符号的右边,这与输出操作符一般的用法相反。为了能够回到正常的语法上来,我们必须把operator<<;移出TextBlock 和 Graphic类,但是如果我们这样做,就不能再把它声明为虚拟了。)
另一种方法是为打印操作声明一个虚拟函数(例如print)把它定义在TextBlock 和 Graphic类里。但是如果这样,打印TextBlock 和 Graphic对象的语法就与使用operator<<;做为输出操作符的其它类型的对象不一致了,这些解决方法都不很令人满意。我们想要的是一个称为operator<<;的非成员函数,其具有象print虚拟函数的行为特性。有关我们想要什么的描述实际上已经很接近如何得到它的描述。我们定义operator<< 和print函数,让前者调用后者! [Page]
class NLComponent {
public:
virtual ostream& print(ostream& s) const = 0;
... 
}; 
class TextBlock: p

本文发布于:2024-09-23 06:29:14,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/435011.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:函数   虚拟   对象
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议