禁止产生栈对象

创建栈对象时会移动栈顶指针以“挪出”适当大小的空间,然后在这个空间上直接调用对应的构造函数以形成一个栈对象,而当函数返回时,会调用其析构函数释放这个对象,然后再调整栈顶指针收回那块栈内存。在这个过程中是不需要operator new/delete操作的,所以将operator new/delete设置为private不能达到目的。当然从上面的叙述中,你也许已经想到了:将构造函数或析构函数设为私有的,这样系统就不能调用构造/析构函数了,当然就不能在栈中生成对象了。

  这样的确可以,而且我也打算采用这种方案。但是在此之前,有一点需要考虑清楚,那就是,如果我们将构造函数设置为私有,那么我们也就不能用new来直接产生堆对象了,因为new在为对象分配空间后也会调用它的构造函数啊。所以,我打算只将析构函数设置为private。再进一步,将析构函数设为private除了会限制栈对象生成外,还有其它影响吗?是的,这还会限制继承。

  如果一个类不打算作为基类,通常采用的方案就是将其析构函数声明为private

  为了限制栈对象,却不限制继承,我们可以将析构函数声明为protected,这样就两全其美了。如下代码所示:

class NoStackObject

{

 protected:

  ~NoStackObject() { }

 public:

  void destroy()

  {

   delete this ;//调用保护析构函数

  }

}; 

  接着,可以像这样使用NoStackObject类:

NoStackObject* hash_ptr = new NoStackObject() ;

... ... //对hash_ptr指向的对象进行操作

hash_ptr->destroy() ; 

  呵呵,是不是觉得有点怪怪的,我们用new创建一个对象,却不是用delete去删除它,而是要用destroy方法。很显然,用户是不习惯这种怪异的使用方式的。所以,我决定将构造函数也设为privateprotected。这又回到了上面曾试图避免的问题,即不用new,那么该用什么方式来生成一个对象了?我们可以用间接的办法完成,即让这个类提供一个static成员函数专门用于产生该类型的堆对象。(设计模式中的singleton模式就可以用这种方式实现。)让我们来看看:

class NoStackObject

{

 protected:

  NoStackObject() { }

  ~NoStackObject() { }

 public:

  static NoStackObject* creatInstance()

  {

   return new NoStackObject() ;//调用保护的构造函数

  }

  void destroy()

  {

   delete this ;//调用保护的析构函数

  }

};

  现在可以这样使用NoStackObject类了:

NoStackObject* hash_ptr = NoStackObject::creatInstance() ;

... ... //对hash_ptr指向的对象进行操作

hash_ptr->destroy() ;

hash_ptr = NULL ; //防止使用悬挂指针 

现在感觉是不是好多了,生成对象和释放对象的操作一致了。

### C++ 上分配对象时隐式调用构造函数的行为解释 在C++中,当一个对象上分配内存并创建时,编译器会根据上下文决定如何调用合适的构造函数。如果存在单参数构造函数,则可能会发生隐式型转换,从而触发该构造函数的调用[^2]。 #### 单参数构造函数的隐式调用 假设有一个 `Person` 定义如下: ```cpp class Person { int m_age; public: Person(int age) : m_age(age) {} // 单参数构造函数 }; ``` 在这种情况下,如果没有显式标记为 `explicit`,则可以将整数型的变量隐式转换为 `Person` 型的对象。例如,在以下代码片段中: ```cpp void someFunction(Person p) {} int main() { someFunction(25); // 隐式调用了 Person 的单参数构造函数 } ``` 这里,整数值 `25` 被用来通过隐式调用 `Person::Person(int)` 构造函数创建了一个临时的 `Person` 对象,并将其传递给 `someFunction()` 函数[^1]。 #### 显式禁止单参数构造函数的隐式调用 为了防止上述行为带来的潜在问题,可以通过在构造函数前加关键字 `explicit` 来禁止隐式型转换。修改后的定义如下所示: ```cpp class Person { int m_age; public: explicit Person(int age) : m_age(age) {} // 使用 explicit 关键字 }; ``` 此时再次尝试运行相同的代码将会导致编译错误,因为不再允许从 `int` 到 `Person` 的隐式转换[^5]。 #### 上的具体表现形式 当对象是在上分配时,其生命周期仅限于当前作用域内。一旦超出此范围,便会自动销毁并释放所占用的空间资源。在此过程中涉及到构造与析构两个阶段的操作。对于简单的数据成员而言,通常不会带来额外负担;然而如果是复杂的数据结构或者动态数组等情况,则可能涉及更多底层细节处理[^4]。 以下是关于上分配的一个例子展示不同情况下的构造过程: ```cpp #include <iostream> using namespace std; class Example { private: string data_; public: Example():data_("default") {cout << "Default Constructor Called."<<endl;} explicit Example(string s):data_(s){cout<<"Parameterized Constructor Called with value:"<<s<<endl;} }; int main(){ Example e1;// 默认构造函数被调用 Example e2{"Explicit"};// 参数化构造函数被显式调用 // 下面这行会产生编译期错误由于我们已经设置了 'explicit' //Example e3 = "Implicit"; return 0; } ``` 在这个实例里面可以看到当我们试图利用字符串常量去初始化一个新的example实例时候如果不采用特定语法就会引发错误提示因为我们先前声明过我们的自定义构造方法需要明确指出才能生效[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值