C#的struct結構具有以下特點:
數據類型:結構是值類型,而類是引用類型。
數據類型:向方法傳遞結構時,結構是通過傳值方式傳遞的,而不是作為引用傳遞的。
實例化:與類不同,結構的實例化可以不使用 new 運算符(未驗證)。
位置:結構可以寫到class中,與方法並列;也可以寫在class外,與類並列。
構造函數:結構可以聲明有參數的構造函數,但它們必須給所有的屬性賦值(this.x=xx;this.y=yy;...)。
若聲明無參的構造函數,編譯期間報錯:編譯器錯誤消息: CS0568: 結構不能包含顯式的無參數構造函數
繼承類:一個結構不能從另一個結構或類繼承,而且不能作為一個類的基。--即,結構不搞基。
繼承自:所有結構都直接繼承自 System.ValueType,后者繼承自 System.Object。
實現接口:結構可以實現接口。
C#之結構struct(轉)
結構是使用 struct 關鍵字定義的,與類相似,都表示可以包含數據成員和函數成員的數據結構。 一般情況下,我們很少使用結構,而且很多人也並不建議使用結構,但作為.NET Framework 一般型別系統中的一個基本架構,還是有必要了解一下的。
結構的特征: 結構是一種值類型,並且不需要堆分配。 結構的實例化可以不使用 new 運算符。
在結構聲明中,除非字段被聲明為 const 或 static,否則無法初始化。 結構類型永遠不是抽象的,並且始終是隱式密封的,因此在結構聲明中不允許使用abstract和sealed修飾符。
結 構不能聲明默認構造函數(沒有參數的構造函數)或析構函數,但可以聲明帶參數的構造函數。 結構可以實現接口,但不能從另一個結構或類繼承,而且不能作為一個類的基,所有結構都直接繼承自System.ValueType,后者繼承 自 System.Object。 結構在賦值時進行復制。 將結構賦值給新變量時,將復制所有數據,並且對新副本所做的任何修改不會更改原始副本的數據。 在使用值類型的集合(如 Dictionary<string, myStruct>)時,請務必記住這一點。 結構類型的變量直接包含了該結構的數據,而類類型的變量所包含的只是對相應數據的一個引用(被引用的數據稱為“對象”)。但是結構仍可以通過ref和 out參數引用方式傳遞給函數成員。 結構可用作可以為 null 的類型,因而可向其賦 null 值。
struct A { public int x; //不能直接對其進行賦值 public int y; public static string str = null; //靜態變量可以初始化 public A(int x,int y) //帶參數的構造函數 { this.x = x; this.y = y; Console.WriteLine("x={0},y={1},str={2}", x, y,str); } } class Program { static void Main(string[] args) { A a = new A(1,2); A a1 = a; a.x = 10; Console.WriteLine("a1.x={0}",a1.x); Console.Read(); } }
結果為:x=1,y=2,str= a1.x=1 此時a1.x值為1是因為,將a賦值給a1是對值進行復制,因此,a1不會受到a.x賦值得改變而改變。
但如果A是類,這時a和a1里的x引用的是同一個地址,則a1.x的值會輸出10。
結構的裝箱與拆箱
我們知道,一個類類型的值可以轉換為 object 類型或由該類實現的接口類型,這只需在編譯時把對應的引用當作另一個類型處理即可。 與此類似,一個object 類型的值或者接口類型的值也可以被轉換回類類型而不必更改相應的引用。當然,在這種情況下,需要進行運行時類型檢查。 由於結構不是引用類型,上述操作對結構類型是以不同的方式實現的。 當結構類型的值被轉換為object 類型或由該結構實現的接口類型時,就會執行一次裝箱操作。 反之,當 object 類型的值或接口類型的值被轉換回結構類型時,會執行一次拆箱操作。 與對類類型進行的相同操作相比,主要區別在於: 裝箱操作會把相關的結構值復制為已被裝箱的實例,而拆箱則會從已被裝箱的實例中復制出一個結構值。 因此,在裝箱或拆箱操作后,對“箱”外的結構進行的更改不會影響已被裝箱的結構。
struct Program { static void Main(string[] args) { int i = 1; object o = i; //隱式裝箱 i = 123; Console.WriteLine("i={0},o={1}",i,o); Console.Read(); } } //結果為:i=123,o=1
結構與構造函數
我們知道結構不能使用默認的構造函數,只能使用帶參數的構造函數,當定義帶參數的 構造函數時,一定要完成結構所有字段的初始化,如果沒有完成所有字段的初始化,編譯時會發生錯誤。 結構可以使用靜態構造函數嗎? 可以,結構的靜態構造函數與類的靜態構造函數所遵循的規則大體相同。 結構的靜態構造函數何時將觸發呢? 結構的實例成員被引用,結構的靜態成員被引用,結構顯示聲明的構造函數被調用。 但是創建結構類型的默認值不會觸發靜態構造函數。為什么結構不能自定義無參數的構造函數? 結構類型的構造函數與類的構造函數類似,用來初始化結構的成員變量,但是struct不能包含顯式默認構造函數, 因為編譯器將自動提供一個構造函數,此構造函數將結構中的每個字段初始化為默認值表中顯示的默認值。 然而,只有當結構用new實例化時,才會調用此默認構造函數。對值類型調用默認構造函數不是必需的。
struct A { static A() { Console.WriteLine("I am A."); } public void Fun() { } } class Program { static void Main(string[] args) { A a=new A(); a.Fun(); //結構的實例成員被引用 Console.Read(); } } 結果為:I am A.
結構與繼承:
一個結構聲明可以指定實現的接口列表,但是不能指定基類。 由於結構不支持類與結構的繼承,所以結構成員的聲明可訪問性不能是 protected 或 protected internal。 結構中的函數成員不能是 abstract 或 virtual,因而 override 修飾符只適用於重寫從 System.ValueType 繼 承的方法。 為在設計編程語言時將結構設計成無繼承性? 其實類的繼承是有相當的成本的 ——由於繼承性,每個類需要用額外的數據空間來存儲“繼承圖”來表示類的傳承歷史, 通俗地說來就是我們人類的家族家譜,里面存儲着我們的祖宗十八代,只有這樣我們才知道我們從哪里來的,而家譜肯定是需要額外的空間來存放的。 大家不要覺得這個存放“繼承圖”的空間很小,如果我們的程序需要用10000個點(Point)來存放游戲中的人物形體數據的話, 在一個場景中又有N個人,這個內存開銷可不是小數目了。所以我們可以通過將點(Point)申明成 Struct而不是class來節約內存空間。
interface ITest { void Fun(int x,int y); } struct A:ITest { public void Fun(int x,int y) //隱式實現接口里的方法 { Console.WriteLine("x={0},y={1}", x, y); } } class Program { static void Main(string[] args) { A a; //結構的實例化可以不使用new a.Fun(1, 2); Console.Read(); } } // 結果為:x=1,y=2
什么情況下結構的實例化可以不使用new?
當結構中沒有參數時,結構的實例化可以不使用new;
當結構中有參數時,必須對結構中所有參數進行初始化后,才能不使用new對結構進行實例化。
什么時候使用結構?
結構體適合一些小型數據結構,這些數據結構包含的數據以創建結構后不修改的數據為主;
例如:struct類型適於表示Point、Rectangle和Color等輕量對象。
盡管可以將一個點表示為類,但在某些情況下,使用結構更有效。
如果聲明一個10000個Point對象組成的數組,為了引用每個對象,則需分配更多內存;這種情況下,使用結構可以節約資源。
定義的時候不會用到面向對象的一些特性;
結構體在不發生裝箱拆箱的情況下性能比類類型是高很多的.
C++的Struct和Class的區別
轉載來源:http://blog.sina.com.cn/s/blog_48f587a80100k630.html
C++中的struct對C中的struct進行了擴充,它已經不再只是一個包含不同數據類型的數據結構了,它已經獲取了太多的功能。
struct能包含成員函數嗎? 能!
struct能繼承嗎? 能!!
struct能實現多態嗎? 能!!!
既然這些它都能實現,那它和class還能有什么區別?
最本質的一個區別就是默認的訪問控制:
默認的繼承訪問權限
struct是public的,class是private的。
你可以寫如下的代碼:
struct A
{
char a;
};
struct B : A
{
char b;
};
這個時候B是public繼承A的。
如果都將上面的struct改成class,那么B是private繼承A的。這就是默認的繼承訪問權限。
所以我們在平時寫類繼承的時候,通常會這樣寫:
class B : public A
就是為了指明是public繼承,而不是用默認的private繼承。
當然,到底默認是public繼承還是private繼承,取決於子類而不是基類。
我的意思是,struct可以繼承class,同樣class也可以繼承struct,那么默認的繼承訪問權限是看子類到底是用的struct還是class。如下:
struct A{};class B : A{}; //private繼承
struct C : B{}; //public繼承
struct作為數據結構的實現體,它默認的數據訪問控制是public的,而class作為對象的實現體,它默認的成員變量訪問控制是private的
我依舊強調struct是一種數據結構的實現體,雖然它是可以像class一樣的用。我依舊將struct里的變量叫數據,class內的變量叫成員,雖然它們並無區別。
到底是用struct還是class,完全看個人的喜好,你可以將程序里所有的class全部替換成struct,它依舊可以很正常的運行。但我給出的最 好建議,還是:當你覺得你要做的更像是一種數據結構的話,那么用struct,如果你要做的更像是一種對象的話,那么用class。
當然,我在這里還要強調一點的就是,對於訪問控制,應該在程序里明確的指出,而不是依靠默認,這是一個良好的習慣,也讓你的代碼更具可讀性。
說到這里,很多了解的人或許都認為這個話題可以結束了,因為他們知道struct和class的“唯一”區別就是訪問控制。很多文獻上也確實只提到這一個區別。
但我上面卻沒有用“唯一”,而是說的“最本質”,那是因為,它們確實還有另一個區別,雖然那個區別我們平時可能很少涉及。
那就是:“class”這個關鍵字還用於定義模板參數,就像“typename”。但關鍵字“struct”不用於定義模板參數。這一點在Stanley B.Lippman寫的Inside the C++ Object Model有過說明。
問題討論到這里,基本上應該可以結束了。但有人曾說過,他還發現過其他的“區別”,那么,讓我們來看看,這到底是不是又一個區別。還是上面所說 的,C++中的struct是對C中的struct的擴充,既然是擴充,那么它就要兼容過去C中struct應有的所有特性。例如你可以這樣寫:
struct A //定義一個struct
{
char c1;
int n2;
double db3;
};
A a={'p', 7, 3.1415926}; //定義時直接賦值
也就是說struct可以在定義的時候用{}賦初值。那么問題來了,class行不行呢?將上面的struct改成class,試試看。報錯!噢~於是那人跳出來說,他又找到了一個區別。我們仔細看看,這真的又是一個區別嗎?
你試着向上面的struct中加入一個構造函數(或虛函數),你會發現什么?
對,struct也不能用{}賦初值了
的確,以{}的方式來賦初值,只是用一個初始化列表來對數據進行按順序的初始化,如上面如果寫成A a={'p',7};則c1,n2被初始化,而db3沒有。這樣簡單的copy操作,只能發生在簡單的數據結構上,而不應該放在對象上。加入一個構造函數或是一個虛函數會使struct更體現出一種對象的特性,而使此{}操作不再有效。
事實上,是因為加入這樣的函數,使得類的內部結構發生了變化。而加入一個普通的成員函數呢?你會發現{}依舊可用。其實你可以將普通的函數理解成對數據結構的一種算法,這並不打破它數據結構的特性。
那么,看到這里,我們發現即使是struct想用{}來賦初值,它也必須滿足很多的約束條件,這些條件實際上就是讓struct更體現出一種數據機構而不是類的特性。
那為什么我們在上面僅僅將struct改成class,{}就不能用了呢?
其實問題恰巧是我們之前所講的——訪問控制!你看看,我們忘記了什么?對,將struct改成class的時候,訪問控制由public變為 private了,那當然就不能用{}來賦初值了。加上一個public,你會發現,class也是能用{}的,和struct毫無區別!!!
做個總結,從上面的區別,我們可以看出,struct更適合看成是一個數據結構的實現體,class更適合看成是一個對象的實現體。