可信网站,普陀网站建设哪家便宜,租点点电脑租赁公司,凡客建站登录C/CLI——2函数与类的使用方法
函数使用
定义函数和使用函数基本与C#相同#xff0c;只不过C/CLI可以像标准C一样#xff0c;可以先声明函数原型#xff0c;再定义函数主体。值得注意的是#xff0c;如果有默认参数#xff0c;只能在函数原型中定义#xff0c;不能在函…C/CLI——2函数与类的使用方法
函数使用
定义函数和使用函数基本与C#相同只不过C/CLI可以像标准C一样可以先声明函数原型再定义函数主体。值得注意的是如果有默认参数只能在函数原型中定义不能在函数主体中重复定义。
#include pch.h
using namespace System;//函数原型
double GetResult(double a1, double b1);//函数主体
double GetResult(double a, double b)
{return a b;
}
//调用函数
int main(arraySystem::String^^ args)
{double r GetResult(100);Console::WriteLine(r);return 0;
}C/CLI中的语句如if、switch、while、for、do while等均与C#用法相同。
类和对象
定义类
和标准C一样在.h头文件中声明原型在.cpp源文件中定义实现。使用::表示C作用域解析符-表示调用成员操作符。
案例
//头文件
#pragma once
ref class CreditCardAccount
{
public:CreditCardAccount();CreditCardAccount(long number, double balance, double limit);static CreditCardAccount();//静态类构造器void SetCreditLimit(double amount);bool MakePurchase(double amount);void MakeRepayment(double amount);void PrintStatement();long GetAccountNumber();static int GetNumberOfAccounts();
private:long accountNumber;double currentBalance;double creditLimit;static int NumberOfAccounts;
};源文件
#include pch.h
#include CreditCardAccount.h
using namespace System;CreditCardAccount::CreditCardAccount()
{
}
静态类构造器
static CreditCardAccount::CreditCardAccount()
{
}
//成员初始化列表
CreditCardAccount::CreditCardAccount(long number, double balance, double limit):accountNumber(number), currentBalance(balance), creditLimit(limit)
{
}
void CreditCardAccount::SetCreditLimit(double amount)
{creditLimit amount;
}
bool CreditCardAccount::MakePurchase(double amount)
{if (currentBalance amountcreditLimit){return false;}else{currentBalance amount;return true;}
}void CreditCardAccount::MakeRepayment(double amount)
{currentBalance - amount;
}void CreditCardAccount::PrintStatement()
{Console::WriteLine(currentBalance);
}long CreditCardAccount::GetAccountNumber()
{return accountNumber;
}
//此时没有static只有在函数原型时含有static
int CreditCardAccount::GetNumberOfAccounts()
{return 0;
}调用
#include pch.h
#include CreditCardAccount.h
using namespace System;int main(arraySystem::String^^ args)
{CreditCardAccount^ myAccount gcnew CreditCardAccount();myAccount-SetCreditLimit(1000);myAccount-MakePurchase(1000);myAccount-PrintStatement();long num myAccount-GetAccountNumber();Console::WriteLine(num);
}类构造器
不同于标准CC/CLI支持静态构造器普通构造器是在对象创建时初始化实例成员而静态构造器是在类可以使用前完成一些准备工作这意味着在创建类的任何对象或者在使用类的任何静态成员之前都会先调用类静态构造函数。
//h文件
ref class CreditCardAccount
{
public:static CreditCardAccount();
};
//cpp文件
static CreditCardAccount::CreditCardAccount()
{}类级常量
类级常量代表在类的所有实例中都不变的值可以使用literal关键字来创建
literal string^ name hello;
在标准的C中可以使用static const来表示类级别常量虽然C/CLI也支持这样写但是如果类时通过一个#using语句来访问这种常量不被认为是编译时常量所以推荐使用literal。
实例常量
可以用initonly关键字来标记实例常量也就是在创建类的实例时由构造器赋值之后就不能更改。
对象生存期
C/CLI中垃圾回收器GC来清理不需要的对象垃圾回收器有三点要注意
对象总是通过句柄来管理这是系统跟踪对象的方式只要有一个句柄指向对象该对象就不会被回收无法判断对象的内存在什么时候回收这有GC来决定
垃圾回收器有一个原则越老的对象存活时间越长因为gc有“代”的概念0代满了就对0代进行回收幸存下来的对象提升到1代目前gc只支持3代。
析构器
标准C和C#都具有析构器只不过C#中的析构器不能显式调用C/CLI定义方式与之相同使用方法和标准C类似使用delete来调用析构器
ref class MyClass
{
public:MyClass();~MyClass();};int main(arraySystem::String^^ args)
{MyClass^ c gcnew MyClass();delete c;
}终结器
垃圾回收器回收对象时调用的是终结器如果使用了非托管资源则要定义终结器如果未使用非托管资源则一般不需要定义终结器与C#中的析构器类似不能显式调用。
ref class MyClass
{
public:MyClass();!MyClass();
};终结期的三个原则
不要定义什么都不做的终结器因为一旦定义了GC就会在回收对象的内存中执行这会有性能损耗终结期的顺序不能确定如A和B都有终结器且都对同一数据进行操作这是无法确定谁先执行的如果整个应用程序已经终止仍然存活的对象不会再调用终结器这包括后台线程使用的对象或者在终结器中创建的对象。
案例
ref class MyClass
{
public:MyClass(String^ name);~MyClass();!MyClass();void DoSomething();private:String^ name;
};MyClass::MyClass(String^ name)
{this-name name;Console::WriteLine(构造函数已调用{0}, name);
}MyClass::~MyClass()
{Console::WriteLine(析构函数调用);
}MyClass::!MyClass()
{Console::WriteLine(终结期调用);
}void MyClass::DoSomething()
{Console::WriteLine(调用方法);
}int main(arraySystem::String^^ args)
{MyClass^ m1 gcnew MyClass(hello);m1-DoSomething();Console::WriteLine(程序结束);
}修改案例
如果在调用时显式调用delete
int main(arraySystem::String^^ args)
{MyClass^ m1 gcnew MyClass(hello);m1-DoSomething();delete m1;Console::WriteLine(程序结束);
}此时程序没有调用终结器因为垃圾回收器认为析构器执行完成后对象已经得到清理不需要执行终结器所以一般要在析构器中显式调用终结器。
MyClass::~MyClass()
{Console::WriteLine(析构函数调用);this-!MyClass();
}在编写C/cli程序时要养成使用对象完毕后调用delete的习惯
栈语义
标准C中可以在栈上直接创建对象
MyClass m(dd);
m.DoSomething();这样在离开作用域后会自动销毁因为是在栈上定义的对象所以说对象具有栈的语义
C/CLI支持相同的方式不过实际上并不是在栈上声明只是为了兼容C的写法目前大多数类型对象都支持栈语义除了字符串和数组这些对象必须使用gcnew来获得。
拷贝构造器
标准C的内存管理严重依赖拷贝构造器如果没有定义则默认提供一个拷贝构造器但是C/CLI有了GC并不会默认提供一个拷贝构造函数而是需要自己写。
MyClass^ a gcnew MyClass();
MyClass^ b a;如何像上面这样写则b和a其实指向的是同一个对象拷贝的只是句柄而不是拷贝指向的对象。如果要完全拷贝则要提供拷贝构造器。
ref class MyClass
{
public:MyClass(const MyClass% other);private:String^ name;int value;
};MyClass::MyClass(const MyClass% other)
{value other.value;name other.name;
}%指定了一个跟踪引用句柄间接引用对象使用-操作符访问成员而跟踪引用只是变量的别名是变量的另一个名字。类似于标准C中的引用只不过为了应对GC操作C/CLI的引用为%。
int i 5;
int %j i; 拷贝构造函数一般使用const来修饰参数这样做的好处主要有2个
引用不产生新的变量减少形参与实参传递时的开销由于引用可能导致实参随形参改变而改变将其定义为常量引用可以消除这种副作用
与标准C一样C/Cli也提供了*解引用符
MyClass^ m gcnew MyClass();
//rm其实和和m其实是指向了同一个对象
MyClass% rm *m;
// mm是具有语义栈的对象其实是调用了拷贝构造器
MyClass mm*m案例
ref class MyClass
{
public:MyClass(const MyClass% other);MyClass(int v);int GetValue();Void SetValue(int v);private:int value;
};MyClass::MyClass(const MyClass% other)
{value other.value;
}MyClass::MyClass(int v)
{value v;
}int MyClass::GetValue()
{return value;
}Void MyClass::SetValue(int v)
{value v;
}int main(arraySystem::String^^ args)
{MyClass^ a gcnew MyClass(1);MyClass^ b a;//仅仅拷贝句柄b-SetValue(10);Console::WriteLine(a的值为{0}, a-GetValue());Console::WriteLine(b的值为{0}, b-GetValue());MyClass% rb *a;//只是用了跟踪引用rb.SetValue(20);Console::WriteLine(a的值为{0}, a-GetValue());Console::WriteLine(rb的值为{0}, rb.GetValue());MyClass c *a;//创建新对象并把它作为a所指对象的拷贝c.SetValue(100);Console::WriteLine(a的值为{0}, a-GetValue());Console::WriteLine(c的值为{0}, c.GetValue());MyClass^ d gcnew MyClass(*a);//直接调用拷贝构造器d-SetValue(1000);Console::WriteLine(a的值为{0}, a-GetValue());Console::WriteLine(d的值为{0}, d-GetValue());Console::WriteLine(程序结束);
}对象和栈语义关联
对象经常由其他对象构成包含对象可以使用栈的语义来声明也可以使用句柄来声明。在使用栈的语义来声明时包含对象是在调用构造函数之前构造的而析构器正好相反。那如何选择是使用栈的语义声明还是句柄来声明要处理以下几个问题
包含对象是否是容器对象的一部分是否能独立存在包含对象是否要与其他对象共享包含对象是否可以与其他对象交换包含对象是否在容器对象销毁后可以继续存活
如果这几个都不满足则优先使用栈的语义声明方式。