本帖最后由 satanness 于 2012-12-24 15:37 编辑
在Windows Store App中,微软决定启用新的C++库,C++/CX便被搬上了舞台。C++/CX与以前听到的C++ 0x/11和C++/CLR并不同。C++ 0x/11是目前最新的C++标准,而C++/CLR是为了让C++开发的东西能够在.Net下运行,即编译后托管于CLR,属于Managed C++。C++/CX属于本地语言(Native C++),没有垃圾回收等其他功能。
好,接下来抱怨一下这个C++/CX。 对于C++比较熟悉的人,都知道C++的学习过程,从C语言开始,然后是面向对象,然后是STL,其他标准库(多线程啊,输入输出啊,等等),说不定还要懂点C++的运行机制才能说自己懂这门语言。这个过程是微软一次次的更改C++的标准库造成的。如今又来了个C++/CX,实在让大家苦不堪言。 不要嫌我抱怨,国外的攻城狮们也都叫苦不迭。传送门里的这篇文章不仅是好文,下面的评论也叫人捧腹,看来微软写的这一笔,叫人开心不起来。
现在C++/CX资料还不算多,除了MSDN论坛上会有微软工程师们不定期地发篇帖子教导一下之外,就只有MSDN上有比较系统、全面的学习了。
废话说得够多了。 本文并不打算跟MSDN一样,系统、全面地介绍C++/CX,毕竟没有人家MSDN来的专业,只介绍一下C++/CX相对于传统C++有什么不同之处。 先从最基本的东西说起。
-基本类型-
基本类型C++/CX & C++对比微软建议,为了使代码简单明了,建议改用 C++/CX 名称。当然,这么些年了,很多东西已经习惯了,要改可不简单。
布尔类型和字符类型C++/CX & C++对比
布尔类型变成了boolean(注意大小写),宽字符变成了char16,当然,要使用以前的名称也是可以的。
-字符串-
字符串String在C++/CX中,属于Platform命名空间。在使用的时候,要加帽子符。String 对象的值是 char16(16 位 Unicode)字符的不可变(只读)序列。由于 String 对象是不可变的,因此将新字符串文本分配给 String 变量其实就是用新的 String 对象替换原始 String 对象。串联运算涉及析构原始 String 对象和创建新对象。跟char[n]数组原理一样,一旦赋值,便不可更改,除非用其他数值或方法替换(strcpy)。
字符串初始化- // Initializing a String^ by using string literals
- String^ str1 = "Test"; // ok for ANSI text only. uses current code page
- String^ str2("Test");
- String^ str3 = L"Test";
- String^ str4(L"Test");
-
- //Initialize a String^ by using another String^
- String^ str6(str1);
- auto str7 = str2;
-
- // Initialize a String from wchar_t* and wstring
- wchar_t msg[] = L"Test";
- String^ str8 = ref new String(msg);
- std::wstring wstr1(L"Test");
- String^ str9 = ref new String(wstr1.c_str());
- String^ str10 = ref new String(wstr1.c_str(), wstr1.length());
复制代码 使用方法有直接初始化,用其他字符串对象初始化,或者用堆区空间初始化。第三种方法的ref new是啥意思先不管,下面会介绍之。
字符串操作- // Concatenation
- auto str1 = "Hello" + " World";
- auto str2 = str1 + " from C++/CX!";
- auto str3 = String::Concat(str2, " and the String class");
-
- // Comparison
- if (str1 == str2) { /* ... */ }
- if (str1->Equals(str2)) { /* ... */ }
- if (str1 != str2) { /* ... */ }
- if (str1 < str2 || str1 > str2) { /* ... */};
- int result = String::CompareOrdinal(str1, str2);
-
- if(str1 == nullptr) { /* ...*/};
- if(str1->IsEmpty()) { /* ...*/};
-
- // Accessing individual characters in a String^
- auto it = str1->Begin();
- char16 ch = it[0];
复制代码 字符串的操作方式也没啥变化。操作符如“+”“=”“>”“<”还是可以正常使用的。
字符串转换- // Create a String^ variable statically or dynamically from a literal string.
- String^ str1 = "AAAAAAAA";
-
- // Use the value of str1 to create the ws1 wstring variable.
- std::wstring ws1( str1->Data() );
- // The value of ws1 is L"AAAAAAAA".
-
- // Manipulate the wstring value.
- std::wstring replacement( L"BBB" );
- ws1 = ws1.replace ( 1, 3, replacement );
- // The value of ws1 is L"ABBBAAAA".
-
- // Assign the modified wstring back to str1.
- str1 = ref new String( ws1.c_str() );
复制代码 字符串的转换也比较简单,多利用一下String类的内部成员函数就可以了。 如果代码接收字符串或字符串文本并仅将其传递给其他方法,则可使用Platform::StringReference^而不是 Platform::String^ 避免创建字符串数据的额外副本。在循环和要传递大量 String^ 实例的情况下,使用 StringReference^ 可提高性能。但目前MSDN上并没有StringReference^类的例子,后面找到相应的例子时再添加。
-枚举-
枚举也有改变。C++/CX 支持 public enum class 关键字,该关键字类似于标准 C++ scoped enum。当使用通过 public enum class 关键字声明的枚举数时,必须使用枚举标识符确定每个枚举值的范围。
- // Define the enum
- public enum class TrafficLight : int { Red, Yellow, Green }; //…
复制代码 当然也可以像如下方式声明:
- public enum class Enum1
- {
- None = 0,
- First, // First == 1
- Some = 5,
- Many = 10
- };
- // Consume the enum:
- TrafficLight myLight = TrafficLight::Red;
- if (myLight == TrafficLight::Green)
- {
- //...
- }
复制代码 可见用法跟以前也不太一样,已经将enum视为一个类了,此类成员权限默认为public。
-ref类和结构-
类的变化算是重头戏。其实早在CLR里,就已经有ref类了,不过属于Managed C++,托管于CLR,所以“血统”不够“纯正”。这次C++的编译器也可以认识这个ref和帽子符“^”了。
ref 类或 ref 结构具有以下基本功能:
1. 它必须在一个命名空间内声明,并且它在该命名空间中可具有公共或私有可访问性。仅将公共类型发送到元数据。(有没有C#的感觉了?)
2. 它可以作为成员 C++/CX 构造或标量类型(如枚举类、ref 类、float64 等)包含。它还可以包含标准 C++ 类型。C++/CX 构造可包含公共、受保护的、内部、私有或受保护的私有可访问性。公共或受保护的成员将发送到元数据。标准 C++ 类型必须具有私有、内部或受保护的私有可访问性,这可阻止将该类型发送到元数据(.winmd)。
3. 它可实现一个或多个“接口类”或“接口结构”。(有没有Java的感觉了?)
4. 它可从一个基类继承,并且基类自身具有附加限制。公共 ref 类层次结构的继承比私有 ref 类中的继承具有更多限制。
5. 它不能声明为泛型。如果它具有私有可访问性,则可以为模板。其生存期由自动引用计数管理。(有没有objective c++的感觉了?)
总的来说,这几种特性让人感觉C++的类已经不是类了,在吸取了几种语言的特性后,变成了一个四不像的生物。当然,这种变化也有好处,比如程序员可以更好地区分类传递是否是“引用传递”;引用计数可以减少内存泄漏;对于内部成员的可访问性有了更严格的规定等等。
类的声明
- namespace PersonaInfo
- {
- [Windows::Foundation::Metadata::WebHostHiddenAttribute]
- ref class Person
- {
- internal:
- Person() {};
- Person(Platform::String^ name);
- public:
- boolean SetPerson(Platform::String^ name, Platform::String^ phoneNumber);
- virtual Platform::String^ SetAddress(Platform::String^ address) sealed;
- Platform::String^ GetName() {return _name;}
- Platform::String^ GetNumber() {return _phoneNumber;}
- private:
- Platform::String^ _name;
- Platform::String^ _phoneNumber;
- };
- }
复制代码 类的实现
- using namespace PersonaInfo;
- Person::Person(Platform::String^ name)
- {
- _name = name;
- _phoneNumber = "";
- }
- Person::~Person() { }
- boolean Person::SetPerson( Platform::String^ name, Platform::String^ phoneNumber )
- {
- _name = name;
- _phoneNumber = phoneNumber;
- return TRUE;
- }
-
- Platform::String^ PersonaInfo::Person::SetAddress( Platform::String^ address )
- {
- return nullptr;
- }
复制代码 类的使用
- Person^ person1 = ref new Person("Pete");
- person1->SetPerson("Steadman", "110");
- Person^ person2 = person1; // Pointing to a same memory location
复制代码
类的继承
- ref class Student sealed: public Person
- {
- public:
- Student() {};
- virtual ~Student() {};
- };
复制代码
看完类的声明、实现及使用后,来详细讲讲变化。
1. 首先是namespace。在C++/CX中,所有的类应当属于某个命名空间,当然了,不加namespace也可以,但是在与页面文件.xaml交流时,没有命名空间可能会非常尴尬。
2. sealed。seal有“封装、密封”之意,基本上有两种用法。
- class class-name sealed {...} // 表示该类不可做为基类被继承。
- virtual return-type Func() sealed {...} // 表示该虚函数不可被重写。
复制代码 3. ref。在某个对象被声明为ref对象时,进行引用传递时,传递的是该对象的句柄而不是该对象本身。这就意味着,上述例子中,person1和person2指向的是同一个内存地址。
4. [Windows::Foundation::Metadata::WebHostHiddenAttribute] 这条语句是为了类的安全性着想的。在C++/CX中,它的名字叫做特性。特性是一类特殊的 ref 类,可在方括号中将其附加在 Windows 运行时类型和方法之前,以指定元数据创建中的某些行为。ref类可包含public、protected和private成员,只有public和protected成员会被发送到元数据,上文提到过。定义公共 ref类或结构时,编译器将必需的特性应用到类,并将该信息存储到应用程序的 .winmd 文件中。但是,当定义公共未密封的 ref 类时,应手动应用[Windows::Foundation::Metadata::WebHostHiddenAttribute]。该特性可确保该类对用 JavaScript 编写的 Windows Store App不可见。
5. 继承。 以下是适用于 C++/CX 中的继承的基本规则:
- 虽然 ref 类最多从一个 ref 基类直接继承,但它可以实现任意数量的接口。
- 如果类具有公共构造函数,则必须将其声明为密封类以防止进一步派生。
- 您可创建具有内部或受保护的私有构造函数的公共未密封基类,前提是该基类从现有未密封基类(如Windows::UI::Xaml::DependencyObject)直接或间接派生。不支持跨 .winmd 文件继承用户定义的 ref 类;但是,ref 类可从其他 .winmd 文件中定义的接口继承。您只能从同一 Windows 运行时组件或 Windows 应用商店应用程序中的用户定义的 ref 基类创建派生类。
- 对于 ref 类,仅支持公共继承。
先说这么多吧,下一篇再讲一下一些新的特性:属性、委托、事件。
|