先回答第三个问题:
包含pragma once语句的文件只会被编译一次。
其他问题的答案下面都有:
我们来看看 C++/CLI中是怎样来定义托管类型的:
如果要创建一个对于在托管堆中对象的应用(reference class),我们可以使用下面的两个关键词:
ref class
ref struct
其中,struct意味着默认情况下它的成员的访问级别为public,而class意味着默认情况下它的成员的访问级别为private。
如果要创建值类型(value class),那么可以使用下面的关键词:
value class
value struct
同样,对于接口,关键词为interface class。
所以,我们就可以看到这样的定义:
public ref class Block { ... };
public value class Vector { ... };
public interface class IMyFile { ... };
如果我们需要指定一个抽象(Abstract)的类,那么语法为:
public ref class Shape abstract {};
public ref class Shape2D abstract : public Shape{};
也可以指定为Sealed类型,以及Sealed和Abstract共用:
public ref class String sealed {};
public ref class State abstract sealed{};
创建了托管类型的对象,就要有指向他们的指针。这里是刚开始学习 C++/CLI的时候,让人有些不习惯的地方,因为定义了一个全新的操作符^。
我们可以先来看看原来的_gc *和新的^的一些具体例子,然后再来感觉那个更好一些:
public __gc class Form1 : public System::Windows::Forms::Form {
private:
System::ComponentModel::Container __gc *components;
Button __gc *button1;
DataGrid __gc *myDataGrid;
DataSet __gc *myDataSet;
void PrintValues( Array* myArr )
{
System::Collections::IEnumerator* myEnumerator =
myArr->GetEnumerator();
Array *localArray = myArr->Copy();
// ...
}
};
public ref class Form1: public System::Windows::Forms::Form{
private:
System::ComponentModel::Container^ components;
Button^ button1;
DataGrid^ myDataGrid;
DataSet^ myDataSet;
void PrintValues( Array^ myArr )
{
System::Collections::IEnumerator^ myEnumerator =
myArr->GetEnumerator();
Array ^localArray = myArr->Copy();
// ...
}
};
是不是^更加自然呢?
可能会有人问:为什么不使用 C++中原来的指针符号*呢?呵呵,因为这是托管代码啊!如果对象定义在非托管堆上,当然使用*了,但如果定义在托管堆上,就不能再简单地使用*了。我们可以看看下面的例子:
Button^ button1 = gcnew Button; // OK: managed heap
int * pi1 = new int; // OK: native heap
interior_ptr<Int32> pi2 = gcnew Int32; // OK: managed heap
这里说明两点:
1. 在 C++/CLI中,同时使用gcnew,来说明对象是创建在托管堆上的。
2. 关于interior_ptr,留待后面解释。
同时,在 C++/CLI中,数字0不再代表空地址,而仅仅代表数字0。空地址有一个关键字:nullptr。所以,下面的代码就是把0给boxing了,再传给指针obj:
Object^ obj=0;
而只有下面的语句,才真正把一个空指针给指针obj:
Object^ obj=nullptr;
顺便,谈谈boxing。boxing就是说,当你想把一个值(value)当作一个对象(object)的时候,发生的事情。例如上面的语句中,就是把一个值0变成了一个object对象。具体在后台发生的事情为:
1. 首先,这个值会被压入堆栈。
2. CLR接着会把这个值弹出堆栈,然后分配一块空间,来存储这个值以及对象头信息。
3. 接着,一个新创建的对象的引用,会被压入堆栈中。
4. 最后,把这个引用对象弹出堆栈,存储在本地变量中。
可见,代价非常大。所以,一般情况下,不要随便使用boxing(当然还有unboxing),因为对性能影响太大。
CLI数组的定义
C++/CLI中,数组定于为:
void PrintValues( array<Object^>^ myArr );
void PrintValues( array<int,3>^ myArr );
同时,还可以使用gcnew定义的时候,直接赋值:
array<Object^>^ myArray =
gcnew array<Object^>(4){ 1, 1, 2, 3 }
而且,还可以定义返回类型为CLI数组的函数:
array<Int32>^ f();
array<int>^ GetArray();
包含pragma once语句的文件只会被编译一次。
其他问题的答案下面都有:
我们来看看 C++/CLI中是怎样来定义托管类型的:
如果要创建一个对于在托管堆中对象的应用(reference class),我们可以使用下面的两个关键词:
ref class
ref struct
其中,struct意味着默认情况下它的成员的访问级别为public,而class意味着默认情况下它的成员的访问级别为private。
如果要创建值类型(value class),那么可以使用下面的关键词:
value class
value struct
同样,对于接口,关键词为interface class。
所以,我们就可以看到这样的定义:
public ref class Block { ... };
public value class Vector { ... };
public interface class IMyFile { ... };
如果我们需要指定一个抽象(Abstract)的类,那么语法为:
public ref class Shape abstract {};
public ref class Shape2D abstract : public Shape{};
也可以指定为Sealed类型,以及Sealed和Abstract共用:
public ref class String sealed {};
public ref class State abstract sealed{};
创建了托管类型的对象,就要有指向他们的指针。这里是刚开始学习 C++/CLI的时候,让人有些不习惯的地方,因为定义了一个全新的操作符^。
我们可以先来看看原来的_gc *和新的^的一些具体例子,然后再来感觉那个更好一些:
public __gc class Form1 : public System::Windows::Forms::Form {
private:
System::ComponentModel::Container __gc *components;
Button __gc *button1;
DataGrid __gc *myDataGrid;
DataSet __gc *myDataSet;
void PrintValues( Array* myArr )
{
System::Collections::IEnumerator* myEnumerator =
myArr->GetEnumerator();
Array *localArray = myArr->Copy();
// ...
}
};
public ref class Form1: public System::Windows::Forms::Form{
private:
System::ComponentModel::Container^ components;
Button^ button1;
DataGrid^ myDataGrid;
DataSet^ myDataSet;
void PrintValues( Array^ myArr )
{
System::Collections::IEnumerator^ myEnumerator =
myArr->GetEnumerator();
Array ^localArray = myArr->Copy();
// ...
}
};
是不是^更加自然呢?
可能会有人问:为什么不使用 C++中原来的指针符号*呢?呵呵,因为这是托管代码啊!如果对象定义在非托管堆上,当然使用*了,但如果定义在托管堆上,就不能再简单地使用*了。我们可以看看下面的例子:
Button^ button1 = gcnew Button; // OK: managed heap
int * pi1 = new int; // OK: native heap
interior_ptr<Int32> pi2 = gcnew Int32; // OK: managed heap
这里说明两点:
1. 在 C++/CLI中,同时使用gcnew,来说明对象是创建在托管堆上的。
2. 关于interior_ptr,留待后面解释。
同时,在 C++/CLI中,数字0不再代表空地址,而仅仅代表数字0。空地址有一个关键字:nullptr。所以,下面的代码就是把0给boxing了,再传给指针obj:
Object^ obj=0;
而只有下面的语句,才真正把一个空指针给指针obj:
Object^ obj=nullptr;
顺便,谈谈boxing。boxing就是说,当你想把一个值(value)当作一个对象(object)的时候,发生的事情。例如上面的语句中,就是把一个值0变成了一个object对象。具体在后台发生的事情为:
1. 首先,这个值会被压入堆栈。
2. CLR接着会把这个值弹出堆栈,然后分配一块空间,来存储这个值以及对象头信息。
3. 接着,一个新创建的对象的引用,会被压入堆栈中。
4. 最后,把这个引用对象弹出堆栈,存储在本地变量中。
可见,代价非常大。所以,一般情况下,不要随便使用boxing(当然还有unboxing),因为对性能影响太大。
CLI数组的定义
C++/CLI中,数组定于为:
void PrintValues( array<Object^>^ myArr );
void PrintValues( array<int,3>^ myArr );
同时,还可以使用gcnew定义的时候,直接赋值:
array<Object^>^ myArray =
gcnew array<Object^>(4){ 1, 1, 2, 3 }
而且,还可以定义返回类型为CLI数组的函数:
array<Int32>^ f();
array<int>^ GetArray();