1. 程式人生 > >例項化類,怎麼限制只能在堆或棧分配!

例項化類,怎麼限制只能在堆或棧分配!

昨天一個同學去網易面試C++研發,問到了這麼一個問題:如何限制一個類物件只在棧(堆)上分配空間?
一般情況下,編寫一個類,是可以在棧或者堆分配空間。但有些時候,你想編寫一個只能在棧或者只能在堆上面分配空間的類。這能不能實現呢?仔細想想,其實也是可以滴。
在C++中,類的物件建立分為兩種,一種是靜態建立,如A a;另一種是動態建立,如A* ptr=new A;這兩種方式是有區別的。
1、靜態建立類物件:是由編譯器為物件在棧空間中分配記憶體,是通過直接移動棧頂指標,挪出適當的空間,然後在這片記憶體空間上呼叫建構函式形成一個棧物件。使用這種方法,直接呼叫類的建構函式。
2、動態建立類物件,是使用new運算子將物件建立在堆空間中。這個過程分為兩步,第一步是執行operator new()函式,在堆空間中搜索合適的記憶體並進行分配;第二步是呼叫建構函式構造物件,初始化這片記憶體空間。這種方法,間接呼叫類的建構函式。

那麼如何限制類物件只能在堆或者棧上建立呢?下面分別進行討論。
1、只能在堆上分配類物件,就是不能靜態建立類物件,即不能直接呼叫類的建構函式。
容易想到將建構函式設為私有。在建構函式私有之後,無法在類外部呼叫建構函式來構造類物件,只能使用new運算子來建立物件。然而,前面已經說過,new運算子的執行過程分為兩步,C++提供new運算子的過載,其實是隻允許過載operator new()函式,而operator new()函式只用於分配記憶體,無法提供構造功能。因此,這種方法不可以。
當物件建立在棧上面時,是由編譯器分配記憶體空間的,呼叫建構函式來構造棧物件。當物件使用完後,編譯器會呼叫解構函式來釋放棧物件所佔的空間。編譯器管理了物件的整個生命週期。如果編譯器無法呼叫類的解構函式,情況會是怎樣的呢?

比如,類的解構函式是私有的,編譯器無法呼叫解構函式來釋放記憶體。所以,編譯器在為類物件分配棧空間時,會先檢查類的解構函式的訪問性,其實不光是解構函式,只要是非靜態的函式,編譯器都會進行檢查。如果類的解構函式是私有的,則編譯器不會在棧空間上為類物件分配記憶體。因此,將解構函式設為私有,類物件就無法建立在棧上了。程式碼如下:

//charles列子
  1. class A  
  2. {  
  3. public:  
  4.     A(){}  
  5.     void destory(){
  6.            delete this;//釋放記憶體。
  7.            }  //用這個來處理delete this。
6. private: 7. ~A(){} //將虛構函式設定為私有變數。 8. }; 9. int main() 10. { 11. A * pa = new A(); 12. delete pa;//會報錯,無法呼叫~A(); 13. pa->destory();//釋放記憶體。 14. return 1; 11. }

試著使用A a;來建立物件,編譯報錯,提示解構函式無法訪問。這樣就只能使用new操作符來建立物件,建構函式是公有的,可以直接呼叫。類中必須提供一個destory函式,來進行記憶體空間的釋放。類物件使用完成後,必須呼叫destory函式。
上述方法的缺點:
一、無法解決繼承問題。如果A作為其它類的基類,則解構函式通常要設為virtual,然後在子類重寫,以實現多型。因此解構函式不能設為private。還好C++提供了第三種訪問控制,protected。將解構函式設為protected可以有效解決這個問題,類外無法訪問protected成員,子類則可以訪問。
二、類的使用很不方便,使用new建立物件,卻使用destory函式釋放物件,而不是使用delete。
(使用delete會報錯,因為delete物件的指標,會呼叫物件的解構函式,而解構函式類外不可訪問)這種使用方式比較怪異。為了統一,可以將建構函式設為protected,然後提供一個public的static函式來完成構造,這樣不使用new,而是使用一個函式來構造,使用一個函式來析構。程式碼如下,類似於單例模式:

  1. class A  
  2. {  
  3. protected:  
  4.     A(){}  
  5.     ~A(){}  
  6. public:  
  7.     static A* create()  
  8.     {  
  9.         return new A();  
  10.     }  
  11.     void destory()  
  12.     {  
  13.         delete this;  
  14.     }  
  15. };  

這樣,呼叫create()函式在堆上建立類A物件,呼叫destory()函式釋放記憶體。

2、只能在棧上分配類物件

只有使用new運算子,物件才會建立在堆上,因此,只要禁用new運算子就可以實現類物件只能建立在棧上。雖然你不能影響new operator的能力(因為那是C++語言內建的),但是你可以利用一個事實:new operator 總是先呼叫 operator new,而後者我們是可以自行宣告重寫的。因此,將operator new()設為私有即可禁止物件被new在堆上。程式碼如下:

  1. class A  
  2. {  
  3. private:  
  4.     void* operator new(size_t t){}     // 注意函式的第一個引數和返回值都是固定的  
  5.     void operator delete(void* ptr){} // 過載了new就需要過載delete  
  6. public:  
  7.     A(){}  
  8.     ~A(){}  
  9. };  

相關推薦

例項怎麼限制只能分配

昨天一個同學去網易面試C++研發,問到了這麼一個問題:如何限制一個類物件只在棧(堆)上分配空間? 一般情況下,編寫一個類,是可以在棧或者堆分配空間。但有些時候,你想編寫一個只能在棧或者只能在堆上面分配空間的類。這能不能實現呢?仔細想想,其實也是可以滴。 在C

根據字串的形式自動匯入模組並使用反射找到模組中的例項物件利用importlib和getattr實現的

例如: auth資料夾下一個SCRF.py檔案,裡面有一個Cors類 class CORS(object): def process_request(self): print('666') auth資料

有父的子例項與父的成員變數建構函式程式碼塊的執行順序

package test; public class SuperC { static int i=10; SuperC(){ print(); } void print(){ System.out.println(i); } }package test; public class

如果的建構函式私有化例項通過靜態成員函式來呼叫建構函式。

靜態私有成員在類外不能被訪問,可通過類的靜態成員函式來訪問; 當類的建構函式是私有的時,不像普通類那樣例項化自己,只能通過靜態成員函式來呼叫建構函式。 物件之間通過類的靜態成員變數來實現資料的共享的。

建立java例項物件

建立java類並例項化類物件例一1.面向物件的程式設計關注於類的設計2.設計類實際上就是設計類的成員3.基本的類的成員,屬性(成員變數)&方法 面向物件思想的落地法則一:1.設計類,並設計類的成員(成員變數&成員方法)2.通過類,來建立類的物件(也稱作類的例項化) public cl

解釋C++例項的指標型別中的new

Intarray * parray = new Intarray();//括號 int * parray = new int(); 兩個都不止是申明,已經初始化了。 第一句是建立(例項化)了一個Intarrya的物件,指標parray指向它。 第二句是建立(分配了)了int型

python:例項物件時提示“TypeError: Employee() takes no arguments”的解決方法

最近開始學習python,學習面向物件的知識時遇到一個問題 在建立例項物件時提示“TypeError: Employee() takes no arguments”,百度翻譯了一下,意思是這個類的建構函式不接受引數 找了半天實在不理解哪裡出問題了,明明都在"_

C# 反射通過例項

在面向物件程式設計的時候,會遇到這樣的問題,一個父類有多個子類,需要建立一個父類的物件,再後面根據條件去把該物件例項化具體的某個子類,然後進行操作。當然用if else 或者switch來做也可以,但是後期擴充套件性不好,特別是要把這些類封裝成dll提供給被人用

php實現例項後自動進行錯誤以及異常處理(簡易版)

<?php class App { public function __construct() { /* * ini_set 設定配置項 * display_errors 是否在頁面顯示錯誤資訊 *

MINE筆記-面向物件程式設計原型鏈的理解建構函式原型物件例項物件prototype 運用

// to do list 建立建構函式,例項化物件,來做面向物件程式設計 // 建立建構函式--原型函式(原型物件) function person(name,age,id){ this.name = name; this.age = age; this.id =id // c

建構函式是什麼有什麼作用例項物件面向物件分析

js裡的面向物件分析-(建立例項化物件) ECMAScript 有兩種開發模式:1.函式式(過程化),2.面向物件(OOP)。面向物件的語言有一個標誌,那就是類的概念,而通過類可以建立任意多個具有相同屬性和方法的物件。但是,ECMAScript 沒有類的概念,因此它的物件也與基於類的語言中的物件

Delphi 通過字串例項

D2007 通過字串建立窗體類物件 1、需要在程式初始化的時候將類註冊,註冊到物件 RegGroups:(TRegGroups)中,以便查詢。 註冊類使用的函式:RegisterClass ,窗體初始化操作放在initialization 中。 un

Python動態例項

  反射機制的工廠方法?反射機制的策略模式? import sys def func1(): print('func1') class TestClass(): def p(self): print('you got me!') def Main():

java 利用反射例項物件

package com.zhiru; /* * java 使用反射例項化一個類物件 * 第一種方式:建立類類物件,呼叫類類物件的newInatance方法獲取要例項化的類的物件,然後呼叫物件的s

Spring框架中使用java反射無法例項,使用ReflectionUtils.findMethod

Spring框架中的反射問題 問題描述 在spring的框架的專案中,使用java的反射去例項化一個service類的時候獲取不到該類的物件. try { Class cla = Class.forName(apiName); //資料庫

學習php反射(2)——不用new方法例項

上一篇簡單介紹了 php 反射的幾個常見類的使用方法,但是用反射能做些什麼,你可能還是想象不到, 下面我稍微應用反射類來做點東西,大家知道例項化一個類需要用new 關鍵字,不用 new 可以嗎?答案是可以的,用反射就能實現: 首先建立一個檔案 student.php

反射呼叫方法例項物件欄位賦值

準備一個類,有參構造方法,欄位,方法都是私有的 public class Car { private String name; private Integer age;

雜談 論例項的第六種方式

你知道幾種例項化一個類的方式? new?反射?還有呢? *******************************************美麗的分割線******************************* 筆者總結了一下大概有以下六種方式: (1)通過構造方法例項化一個類; (2)通過Class例

Server Technology的這個新電源插座可以充當C13C19插座

計劃 open 圖片 nag dot 分享圖片 用戶 選擇 認證 Legrand子公司Server Technology(服務器技術公司)在其配電單元(PDU)中引入了一個新的電源插座,可以充當C13或C19插座,為用戶提供了更多的靈活性。 服務器技術公司的工程總監Calv

共享單車專案的Python視覺分析教你成為資料分析大師

Python對資料的處理能力,很多人是抱著一定的懷疑的,不過在看完這篇文章之後,我相信你一定不會再懷疑的。 一、背景: 共享單車想必大家一定不會陌生,共享單車在國內的興起,應該是2014年ofo的創立。截止到2017年3月,中國共享單車數量已經達到400萬輛,成為大城市居民出行