-
如果一个类A里面声明的一个友元类B,那么这个被声明的友元类B的全部函数对于A都是友元函数,即可以访问A中所有成员的函数
#include<iostream> class MajorisA { private: double Light = 0; double Radius = 0; public: friend class MajorisB; MajorisA(double l, double r) :Light(l), Radius(r) {}; void ShowLight() { std::cout << "A: " << Light << std::endl; } void ShowRadius() { std::cout << "A: " << Radius << std::endl; } }; class MajorisB { private: double Light = 0; double Radius = 0; public: MajorisB(double l, double r) :Light(l), Radius(r) {}; void ShowLight(const MajorisA& a) { std::cout << "B: " << a.Light << " " << Light << std::endl; } void ShowRadius(const MajorisA& a) { std::cout << "B: " << a.Radius << " " << Radius << std::endl; } }; int main() { MajorisA a(5.0, 10.0); MajorisB b(1.0, 2.0); a.ShowLight(); a.ShowRadius(); b.ShowLight(a); b.ShowRadius(a); return 0; }
-
如果只希望B类中部分成员函数成为A的友元函数,可以如下实现:(注:需要声明A类,这样才能在B类中声明有关A类的函数,如果先定义A类,再定义B类,这会导致A类编译到友元函数GetLight时无法在B类中查找到相关函数声明,导致编译错误)
#include<iostream> class MajorisA; class MajorisB { private: double Light = 0; double Radius = 0; public: MajorisB(double l, double r) :Light(l), Radius(r) {}; void ShowLight() { std::cout << "B: " << Light << std::endl; } void ShowRadius() { std::cout << "B: " << Radius << std::endl; } void GetLight(const MajorisA& a); }; class MajorisA { private: double Light = 0; double Radius = 0; int data = 0; public: friend void MajorisB::GetLight(const MajorisA& a); MajorisA(double l, double r) :Light(l), Radius(r) {}; void ShowLight() { std::cout << "A: " << Light << std::endl; } void ShowRadius() { std::cout << "A: " << Radius << std::endl; } }; void MajorisB::GetLight(const MajorisA& a) { std::cout << "Fri: " << a.Light << " " << a.Radius << std::endl; } int main() { MajorisA a(10.0, 50.0); MajorisB b(5.0, 20.0); a.ShowLight(); a.ShowRadius(); b.ShowLight(); b.ShowRadius(); b.GetLight(a); return 0; }
-
CPP还可以声明共同友元,即在A类和B类中声明两个或者多个一样的友元函数:
#include<iostream> class MajorisA; class MajorisB { private: double Light = 0; double Radius = 0; public: MajorisB(double l, double r) :Light(l), Radius(r) {}; void ShowLight() { std::cout << "B: " << Light << std::endl; } void ShowRadius() { std::cout << "B: " << Radius << std::endl; } friend void SetLight(const MajorisA& a, MajorisB& b); friend void SetLight(const MajorisB& b, MajorisA& a); }; class MajorisA { private: double Light = 0; double Radius = 0; int data = 0; public: friend void SetLight(const MajorisA& a, MajorisB& b); friend void SetLight(const MajorisB& b, MajorisA& a); MajorisA(double l, double r) :Light(l), Radius(r) {}; void ShowLight() { std::cout << "A: " << Light << std::endl; } void ShowRadius() { std::cout << "A: " << Radius << std::endl; } }; void SetLight(const MajorisA& a, MajorisB& b) { b.Light = a.Light; } void SetLight(const MajorisB& b, MajorisA& a) { a.Light = b.Light; } int main() { MajorisA a(10.0, 50.0); MajorisB b(5.0, 20.0); a.ShowLight(); a.ShowRadius(); b.ShowLight(); b.ShowRadius(); std::cout << std::endl; SetLight(a, b); a.ShowLight(); a.ShowRadius(); b.ShowLight(); b.ShowRadius(); MajorisA c(100.0, 100.0); SetLight(b, c); std::cout << std::endl; c.ShowLight(); c.ShowRadius(); b.ShowLight(); b.ShowRadius(); return 0; }
-
CPP可以使用嵌套类,即在一个类中定义一个类,其中后者被private,public,protected修饰的效果类似于被前者定义的数据成员被这三中修饰符其中一个修饰的效果:
#include<iostream> class Queue { public: class Node { public: int item; Node* next; Node(const int& i); }; bool enqueue(const int& item); }; Queue::Node::Node(const int& i) :item(i), next(0) {}; bool Queue::enqueue(const int& item) { //if(isfull()) return false; Node* add = new Node(item); //...... return true; } int main() { return 0; }
当然嵌套类也可以嵌套到模板中:
template<class ITEM> class Queue { public: class Node { public: ITEM item; Node* next; Node(const ITEM i); }; bool enqueue(const ITEM& item); }; template<class ITEM> Queue<ITEM>::Node::Node(const ITEM i) :item(i), next(0) {}; template<class ITEM> bool Queue<ITEM>::enqueue(const ITEM& item) { //if(isfull()) return false; Node* add = new Node(item); //...... return true; }
-
CPP也有类似于CS的异常处理,注throw可以是任何类型的数据,包括class,而catch的()只会捕获()中类型的数据,一旦抛出异常,catch会捕获相关类型异常,如果抛出的异常没有被对应的catch检测到,则vs会终止程序运行。
#include<iostream> double hmean(double a, double b); int main() { double x, y, z; std::cout << "Enter next set of numbers <q to quit>; "; while (std::cin>>x>>y) { try { z = hmean(x, y); } catch (const char* exc) { std::cout << exc << std::endl; std::cout << "Enter a new pair of numbers"; continue; } std::cout << "Harmonic mean of " << x << " and " << y << "is " << z << std::endl; std::cout << "Enter next set of numbers <q to quit>; "; } std::cout << "Bye!\n"; return 0; } double hmean(double a, double b) { if (a == -b) throw "bad hmean() arguments: a = -b not allowed"; return 2.0 * a * b / (a + b); }
Enter next set of numbers <q to quit>; 2 5 Harmonic mean of 2 and 5is 2.85714 Enter next set of numbers <q to quit>; 2 -2 bad hmean() arguments: a = -b not allowed Enter a new pair of numbers3 3 Harmonic mean of 3 and 3is 3 Enter next set of numbers <q to quit>; 4.23 -4.23 bad hmean() arguments: a = -b not allowed Enter a new pair of numbers4.123123 -4.123123 bad hmean() arguments: a = -b not allowed Enter a new pair of numbers4.123123123123 -4.123123123123 bad hmean() arguments: a = -b not allowed Enter a new pair of numbersq Bye!
Enter two numbers: 2 -2 hmean (2, -2); invalid arguments: a = -b Try again. 3 4 Harmonic mean of 3 and 4 is 3.42857 Geometric mean of 4 and 4 is 3.4641 Enter next set of numbers <q to quit>:-1 1 hmean (-1, 1); invalid arguments: a = -b Try again. -1 2 Harmonic mean of -1 and 2 is -4 gmean() arguments should be >= 0 Value used:-1, 2 Sorry, you don't get to play any more. Bye!
-
CPP栈解退,假设程序中某个函数因抛出异常而终止,程序将释放该函数在栈中的内存,然后继续释放调用该函数的函数在栈中的内存,以此类推,直到执行到try块语句,然后控制权将转到块尾的异常控制程序,在这个多个函数不断释放的过程中,对于栈中局部变量的类对象,类的析构函数将被调用。注:抛出异常指的是throw,如果在catch语块中的throw后面不接语句,会抛出catch所捕获的异常,如果不在catch语块中的throw后面不接语句,会抛出std::exception的异常,(std::exception是CPP库中的一个类,用于表示通用的异常情况)
#include<iostream> #include<cmath> #include<string> class bad_hmean { private: double v1; double v2; public: bad_hmean(double a = 0, double b = 0) :v1(a), v2(b) {}; void mesg(); }; inline void bad_hmean::mesg() { std::cout << "hmean (" << v1 << ", " << v2 << "); " << "invalid arguments: a = -b\n"; } class bad_gmean { public: double v1; double v2; bad_gmean(double a = 0, double b = 0) :v1(a), v2(b) {}; const char* mesg(); }; inline const char* bad_gmean::mesg() { return "gmean() arguments should be >= 0\n"; } class demo { private: std::string word; public: demo(const std::string& str) { word = str; std::cout << "demo " << word << " created\n"; } ~demo() { std::cout << "demo " << word << " destroted\n"; } void show() const { std::cout << "demo " << word << " lives\n"; } }; double hmean(double a, double b); double gmean(double a, double b); double means(double a, double b); int main() { double x, y, z; std::cout << "Enter two numbers: "; demo d1("Found in block in main()"); while (std::cin >> x >> y) { try { z = means(x, y); std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl; std::cout << "Geometric mean of " << y << " and " << y << " is " << z << std::endl; std::cout << "Enter next set of numbers <q to quit>:"; } catch (bad_hmean& hg) { hg.mesg(); std::cout << "Try again.\n"; continue; } catch (bad_gmean& bg) { std::cout << bg.mesg(); std::cout << "Value used:" << bg.v1 << ", " << bg.v2 << std::endl; std::cout << "Sorry, you don't get to play any more.\n"; break; } } d1.show(); std::cout << "Bye!\n"; std::cin.get(); std::cin.get(); return 0; } double hmean(double a, double b) { if (a == -b) throw bad_hmean(a, b); return 2.0 * a * b / (a + b); } double gmean(double a, double b) { if (a * b < 0) throw bad_gmean(a, b); return std::sqrt(a * b); } double means(double a, double b) { double am, hm, gm; demo d2("found in means()"); am = (a + b) / 2.0; try { hm = hmean(a, b); gm = gmean(a, b); } catch (bad_hmean& bg) { bg.mesg(); std::cout << "Caught in means\n"; throw; } d2.show(); return (am + hm + gm) / 3.0; }
Enter two numbers: demo Found in block in main() created 2 2 demo found in means() created demo found in means() lives demo found in means() destroted Harmonic mean of 2 and 2 is 2 Geometric mean of 2 and 2 is 2 Enter next set of numbers <q to quit>:-4 4 demo found in means() created hmean (-4, 4); invalid arguments: a = -b Caught in means demo found in means() destroted hmean (-4, 4); invalid arguments: a = -b Try again. -4 -4 demo found in means() created demo found in means() lives demo found in means() destroted Harmonic mean of -4 and -4 is -1.33333 Geometric mean of -4 and -4 is -1.33333 Enter next set of numbers <q to quit>:-4 1 demo found in means() created demo found in means() destroted gmean() arguments should be >= 0 Value used:-4, 1 Sorry, you don't get to play any more. demo Found in block in main() lives Bye! 1 demo Found in block in main() destroted
-
引发异常时编译器总是创建一个临时的拷贝,即使异常规范和catch块中指定的是引用,如下面的代码,catch将指向oops的副本,而不是oops本身,在释放函数内存后,oops将销毁,但是catch仍然使用引用,这是因为基类引用可以执行派生类对象,假设如果有一连串通过继承来关联的异常类型,则catch()中只需要写基类的引用,这将于任何派生类对象匹配,但是由于这种方法使得基类可以捕获派生类异常,一般catch语块排列顺序为将捕获位于层次结构最下面的异常类的catch语句放在最前面。
ckass problem{...}; ... void super() throw(problem) //函数原型后面写throw(xxx)提示使用super的话很可能会抛出什么类型异常 //如果throw的()里面没有东西,则意味着函数不会引发什么异常。 { if(oh_no) { problem oops; throw oops; } } ... try{ super(); } catch(problem& p) { //statement; }
class bad_1{}; class bad_2:public bad_1{}; class bad_3:public bad_2{}; void duper() { if(oh_no) throw bad_1(); if(rats) throw bad_2(); if(drat) throw bad_3(); } try { duper(); } catch(bad_3& be) {} catch(bad_2& be) {} catch(bad_1& be) {} catch(...) //这里可以使用省略号,类似于switch中的deflaut,可以捕获任何异常 {}
-
类型转换运算符:
- dynamic_cast运算符:如果该运算符指向的对象(pt)类型为Type或者是从Type直接或者间接派生而来的类型,则下面的表达式将从指针pt转换为Type类型的指针,否则转化为空指针:dynamic_cast<Type*>(pt),使用dynamic注意基类转换成派生类时,基类或者被基类继承的其他类需要一个virtual函数保证类时多态类型
- typeid运算符:该运算符能够确定两个对象是否为同一个类型,可接受类名以及结果为对象的表达式,其返回的是type_info对象引用
#include<iostream> #include<typeinfo> class Base { virtual void Func(int a) { std::cout << a << std::endl; } }; class Superb :public Base { }; class Magnificent :public Superb { }; int main() { std::cout << typeid(Magnificent).name() << std::endl; Base* ptr1 = new Base; Base* ptr2 = new Superb; Base* ptr3 = new Magnificent; Superb* ptr4 = nullptr; if (ptr4 = dynamic_cast<Superb*>(ptr1)) { std::cout << "Succeed for prt4 convert to ptr1" << std::endl; } else { std::cout << "Fail for ptr4 convert to ptr1" << std::endl; } if (ptr4 = dynamic_cast<Superb*>(ptr2)) { std::cout << "Succeed for prt4 convert to ptr2" << std::endl; } else { std::cout << "Fail for ptr4 convert to ptr2" << std::endl; } if (ptr4 = dynamic_cast<Superb*>(ptr3)) { std::cout << "Succeed for prt4 convert to ptr3" << std::endl; } else { std::cout << "Fail for ptr4 convert to ptr3" << std::endl; } if (typeid(1 + 3.0) == typeid(int)) { std::cout << "Succeed for 1 + 2 convert to int" << std::endl; } else { std::cout << "Fail for 1 + 2 convert to int " << bool(typeid(1 + 3.0) != typeid(int)) << std::endl; } if (typeid(Magnificent) == typeid(*ptr3)) { std::cout << "Succeed for Magnificent compare to *ptr2" << std::endl; } else { std::cout << "Fail for Magnificent compare to *ptr2" << std::endl; } delete ptr1; delete ptr2; delete ptr3; return 0; }
class Magnificent Fail for ptr4 convert to ptr1 Succeed for prt4 convert to ptr2 Succeed for prt4 convert to ptr3 Fail for 1 + 2 convert to int 1 Succeed for Magnificent compare to *ptr2
- const_cast用于将被const和volatile修饰的变量类型转换,const_cast<type-name>(expression),expression和type-name除了const和volatile特征可以不同外,二者类型必须相同。
#include<iostream>
#include<typeinfo>
class Base
{
};
class Superb :public Base
{
};
class Magnificent :public Superb
{
};
int main()
{
Base base1;
const Base* base2 = &base1;
Base* ptr1 = const_cast<Base*> (base2);//valid
Base* ptr2 = (Base*)base2;//valid but dangerous
//const Superb* ptr2 = const_cast<const Superb*>(ptr1);//invalid
return 0;
}
- static_cast<type-name>(expression)运算符仅当type_name可被隐式转换为expression所属的类型或者expression可被隐式转换为type_name所属类型时,上述转换才合法。
#include<iostream> #include<typeinfo> class Base { }; class Superb :public Base { }; class Magnificent :public Superb { }; int main() { Base* base1 = new Base; Superb* superb1 = new Superb; Magnificent* magnificent1 = new Magnificent; Base* base2; Superb* superb2; Magnificent* magnificent2; if (base2 = static_cast<Base*>(superb1)) { std::cout << "Succeed for convert base2 to superb2" << std::endl; } else { std::cout << "Failed for convert base2 to superb2" << std::endl; } if (base2 = static_cast<Base*>(magnificent1)) { std::cout << "Succeed for convert base2 to magnificent2" << std::endl; } else { std::cout << "Failed for convert base2 to magnificent2" << std::endl; } if (magnificent2 = static_cast<Magnificent*>(base1)) { std::cout << "Succeed for convert magnificent2 to base1" << std::endl; } else { std::cout << "Failed for convert magnificent2 to base1" << std::endl; } if (magnificent2 = static_cast<Magnificent*>(superb1)) { std::cout << "Succeed for convert magnificent2 to superb1" << std::endl; } else { std::cout << "Failed for convert magnificent2 to superb1" << std::endl; } delete base1; delete superb1; delete magnificent1; return 0; }
- 构造函数初始化适用于初始化引用类型和const修饰的常变量,而且这两者都需要被类创建对象的时候初始化。
class Exercise { private: int& a; const int b; int c; public: Exercise(int x, int y, int z) :a(x), b(y), c(z) {}; };