友元的拓展语法
声明一个类为另外一个类的友元时,不再需要使用class关键字,并且还可以使用类的别名(使用 typedef 或者 using 定义)。
cpp
#include <iostream>
using namespace std;
// 类声明
class Tom;
// 定义别名
using Honey = Tom;
// 定义两个测试类
class Jack
{
// 声明友元
// friend class Tom; // C++98 标准语法
friend Tom; // C++11 标准语法
string name = "jack"; // 默认私有
void print() // 默认私有
{
cout << "my name is " << name << endl;
}
};
class Lucy
{
protected:
// 声明友元
// friend class Tom; // C++98 标准语法
friend Honey; // C++11 标准语法
string name = "lucy";
void print()
{
cout << "my name is " << name << endl;
}
};
class Tom
{
public:
void print()
{
// 通过类成员对象访问其私有成员
cout << "invoke Jack private member: " << jObj.name << endl;
cout << "invoke Jack private function: " << endl;
jObj.print();
cout << "invoke Lucy private member: " << lObj.name << endl;
cout << "invoke Lucy private function: " << endl;
lObj.print();
}
private:
string name = "tom";
Jack jObj;
Lucy lObj;
};
int main()
{
Tom t;
t.print();
return 0;
}
类模板的友元应用
在c++98时不可以为类模板声明友元,c++11新特性是可以声明为友元的
基本用法
cpp
class Tom;
template<typename T>
class Person
{
friend T;
};
int main()
{
Person<Tom> p;// >>>>>>>>>> Tom类是Person类的友元
Person<int> pp;// >>>>>>>>>>> 对于int类型的模板参数,友元声明被忽略(第6行)
return 0;
}
复杂案例
cpp
#include<iostream>
using namespace std;
template<typename T>
class Rectangle
{
public:
friend T;
Rectangle(int w, int h) : width(w), height(h) {}
private:
int width;
int height;
};
template<typename T>
class Circle
{
public:
friend T;
Circle(int r) : radius(r) {}
private:
int radius;
};
// 校验类
class Verify
{
public:
void verifyRectangle(int w, int h, Rectangle<Verify>& r)
{
if (r.width >= w && r.height >= h)
{
cout << "矩形的宽度和高度满足条件!" << endl;
}
else
{
cout << "矩形的宽度和高度不满足条件!" << endl;
}
}
void verifyCircle(int r, Circle<Verify>& c)
{
if (r >= c.radius)
{
cout << "圆形的半径满足条件!" << endl;
}
else
{
cout << "圆形的半径不满足条件!" << endl;
}
}
};
int main()
{
Verify v;
Circle<Verify> circle(30);
Rectangle<Verify> rect(90, 100);
v.verifyCircle(60, circle);
v.verifyRectangle(100, 100, rect);
return 0;
}
强类型枚举
枚举的缺陷
cpp
// 匿名枚举
enum {Red, Green, Blue};
// 有名枚举
enum Colors{Red, Green, Blue};
如果这个时候在main函数下执行:
cpp
cout<<"red:"<<red<<endl;//err 属于重定义
错误的原因:在这两个枚举中Red是全局可见的,所有编译器就会提示其重定义了。
强类型枚举语法
针对枚举的缺陷,C++11标准引入了一种新的枚举类型,即枚举类,又称强类型枚举(strong-typed enum)。 声明强类型枚举非常简单,只需要在 enum 后加上关键字 class。
强类型枚举特性
- 强作用域,强类型枚举成员的名称不会被输出到其父作用域空间。
- 强类型枚举只能是
有名枚举
,如果是匿名枚举会导致枚举值无法使用(因为没有作用域名称)。
转换限制,强类型枚举成员的值不可以与整型隐式地相互转换。- 可以指定底层类型。强类型枚举默认的底层类型为 int,但也可以显式地指定底层类型
具体方法为在枚举名称后面加上∶type,其中 type 可以是除 wchar_t 以外的任何整型。比如:
cpp
#include<iostream>
using namespace std;
enum class Color:char{ Red, Green, Blue };
enum struct Color1 { Red, Green, Blue };
int main()
{
cout << "red:" << (int)Color::Red << endl;
int m = (int)Color::Green;
cout << "green:" << (int)Color::Green << endl;
cout << "sizeof Color:char" << sizeof(Color) << endl;
cout << "sizeof Color1:" << sizeof(Color1) << endl;
return 0;
}
对原有枚举的拓展
原有枚举类型的底层类型在默认情况下,仍然由编译器来具体指定实现。但也可以跟强类型枚举类一样,显式地由程序员来指定。其指定的方式跟强类型枚举一样,都是枚举名称后面加上∶type,其中type 可以是除 wchar_t 以外的任何整型
cpp
#include<iostream>
using namespace std;
enum TestColor:char { Red='a', Green, Blue };//>>>>>>普通枚举是可以直接访问的
enum class Color:char{ Red, Green, Blue };
enum struct Color1 { Red, Green, Blue };
int main()
{
cout << "TestColor Red:" << Red << endl
<< "TestColor Red:" << TestColor::Red << endl;
cout << "sizeof TestColor:" << sizeof(TestColor) << endl;
cout << "Red:" << (int)Color::Red << endl;
int m = (int)Color::Green;
cout << "green:" << (int)Color::Green << endl;
cout << "sizeof Color:char" << sizeof(Color) << endl;
cout << "sizeof Color1:" << sizeof(Color1) << endl;
return 0;
}
非受限联合体
静态类型的数据/成员在非受限联合体的使用
在c++11里面不受限的是:静态类型的成员、POD类型
对于引用还是受限制的
cpp
#include<iostream>
using namespace std;
union Test
{
int age;
long id;
// int& tmp = age; // error >>>>>>>>> 非受限联合体中不允许出现引用类型
static char c;
static int print()
{
cout << "c value: " << c << endl;
return 0;
}
};
char Test::c;
// char Test::c = 'a';
int main()
{
Test t;
Test t1;
t.c = 'b';
t1.c = 'c';
t1.age = 666;
cout << "t.c: " << t.c << endl;
cout << "t1.c: " << t1.c << endl;
//在联合体中,所有成员共享相同的内存空间。所以,当你给某个成员赋值时,其他成员的值也会被改变
// //在非受限联合体中静态成员变量和非静态成员变量使用的不是同一块内存
cout << "t1.age: " << t1.age << endl;
cout << "t1.id: " << t1.id << endl;
t.print();
Test::print();
return 0;
}
POD类型在非受限联合体的使用
union Student 中包含了一个 string 类型成员,而 std::string 是一个复杂的类类型,具有构造函数、析构函数和其他成员函数。由于 string 类型的存在,Student 联合体不能被视为 POD 类型。
如果原本类中的是POD类型,如果这个时候添加了一段自定义的构造函数那么此时的类就不是POD类型
cpp
union Student
{
int id;
string name;
};
int main()
{
Student s;//err
return 0;
}
由于非POD类型在非受限联合体的使用,这个联合体的构造函数就会被删除。
解决方案:手动地去指定一块内存空间
即-->使用placement new
placement new
- 使用new申请内存空间:Base* ptr = new Base;
- 使用定位放置new申请内存空间:
ClassName* ptr = new (定位的内存地址)ClassName;
简单案例
cpp
#include <iostream>
using namespace std;
class Base
{
public:
Base() {}
~Base() {}
void print()
{
cout << "number value: " << number << endl;
}
private:
int number;
};
int main()
{
int n = 100;
Base* b = new (&n)Base;// >>>>> 使用placement new指定栈内存(n的)为存储空间
b->print();
return 0;
}
- 使用定位放置new操作,既可以在栈(stack)上生成对象,也可以在堆(heap)上生成对象,这取决于定位时指定的内存地址是在堆还是在栈上。
- 从表面上看,定位放置new操作是申请空间,其本质是利用已经申请好的空间,真正的申请空间的工作是在此之前完成的。
- 使用定位放置new 创建对象时会自动调用对应类的构造函数,但是由于对象的空间不会自动释放,如果需要释放堆内存必须显示调用类的析构函数。
- 使用定位放置new操作,我们可以反复动态申请到同一块堆内存,这样可以避免内存的重复创建销毁,从而提高程序的执行效率(比如网络通信中数据的接收和发送)
复杂案例
cpp
#include<iostream>
using namespace std;
class Base
{
public:
void setText(string str)
{
notes = str;
}
void print()
{
cout << "Base notes: " << notes << endl;
}
private:
string notes;
};
union Student
{
Student()
{
new (&name)string;//选取占内存最大的
}
~Student() {}
int id;
Base tmp;
string name;
};
int main()
{
Student s;
s.name = "蒙奇·D·路飞";
cout << "Student name: " << s.name << endl;
s.tmp.setText("我是要成为海贼王的男人!");
s.tmp.print();
cout << "Student name: " << s.name << endl;//>>>>>>共用同一块内存
return 0;
}
匿名的非受限联合体
cpp
#include<iostream>
using namespace std;
/*
木叶村要进行第99次人口普查,人员的登记方式如下:
- 学生只需要登记所在学校的编号
- 本村学生以外的人员需要登记其身份证号码
- 本村外来人员需要登记户口所在地+联系方式
*/
// 外来人口信息
struct Foreigner
{
Foreigner(string s, string ph) : addr(s), phone(ph) {}
string addr;
string phone;
};
// 人口信息
class Person
{
public:
//枚举出人员身份
enum class Category : char { Student, Local, Foreign };
Person(int num) : number(num), type(Category::Student) {}
Person(string id) : idNum(id), type(Category::Local) {}
Person(string addr, string phone) : foreign(addr, phone), type(Category::Foreign) {}
~Person() {}
void print()
{
cout << "Person category: " << (int)type << endl;
switch (type)
{
case Category::Student:
cout << "Student school number: " << number << endl;
break;
case Category::Local:
cout << "Local people ID number: " << idNum << endl;
break;
case Category::Foreign:
cout << "Foreigner address: " << foreign.addr
<< ", phone: " << foreign.phone << endl;
break;
default:
break;
}
}
private:
Category type;
union
{
int number;
string idNum;
Foreigner foreign;
};
};
int main()
{
Person p1(9527);
Person p2("1101122022X");
Person p3("砂隐村村北", "1301810001");
p1.print();
p2.print();
p3.print();
return 0;
}