装修的网站,wordpress wdpx,ipv6网站建设,杨浦苏州网站建设目录 一、友元
1、定义
2、友元函数
3、友元类
二、内部类
1、定义
2、特性#xff1a;
三、匿名对象
四、拷贝对象时的一些编译器优化
1、传值传引用返回优化对比
2、匿名对象作为函数返回对象
3、接收返回值方式对比
总结#xff1a; 一、友元
1、定义…目录 一、友元
1、定义
2、友元函数
3、友元类
二、内部类
1、定义
2、特性
三、匿名对象
四、拷贝对象时的一些编译器优化
1、传值传引用返回优化对比
2、匿名对象作为函数返回对象
3、接收返回值方式对比
总结 一、友元
1、定义 友元提供了一种突破封装的方式有时提供了便利。但是友元会增加耦合度破坏了封装所以友元不宜多用。 友元分为友元函数和友元类
2、友元函数 问题现在尝试去重载operator然后发现没办法将operator重载成成员函数使用形式发生变化。 class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}// d1 cout; 或者 d1.operator(d1, cout); 不符合常规调用// 因为成员函数第一个参数一定是隐藏的this所以d1必须放在的左侧ostream operator(ostream _cout){_cout _year - _month - _day endl;return _cout;}
private:int _year;int _month;int _day;
};因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象才能正常使用。所以要将operator重载成全局函数。但又会导致类外没办法访问成员此时就需要友元来解决。operator同理。 友元函数可以直接访问类的私有成员它是定义在类外部的普通函数不属于任何类但需要在类的内部声明声明时需要加friend关键字。 class Date
{friend ostream operator(ostream _cout, const Date d);friend istream operator(istream _cin, Date d);
public:Date(int year 1900, int month 1, int day 1): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};
ostream operator(ostream _cout, const Date d)
{_cout d._year - d._month - d._day;return _cout;
}
istream operator(istream _cin, Date d)
{_cin d._year;_cin d._month;_cin d._day;return _cin;
}
int main()
{Date d;cin d;cout d endl;return 0;
}
友元函数可访问类的私有和保护成员但不是类的成员函数 友元函数不能用const修饰 友元函数可以在类定义的任何地方声明不受类访问限定符限制 一个函数可以是多个类的友元函数 友元函数的调用与普通函数的调用原理相同
3、友元类 友元类的所有成员函数都可以是另一个类的友元函数都可以访问另一个类中的非公有成员。 class Time
{friend class Date; // 声明日期类为时间类的友元类则在日期类中就直接访问Time类中的私有成员变量
public:Time(int hour 0, int minute 0, int second 0): _hour(hour), _minute(minute), _second(second){}private:int _hour;int _minute;int _second;
};
class Date
{
public:Date(int year 1900, int month 1, int day 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_time._hour hour;_time._minute minute;_time._second second;}private:int _year;int _month;int _day;Time _time;
}
友元关系是单向的不具有交换性。 比如上述Time类和Date类在Time类中声明Date类为其友元类那么可以在Date类中直接访问Time类的私有成员变量但想在Time类中访问Date类中私有的成员变量则不行。 友元关系不能传递。如果C是B的友元 B是A的友元则不能说明C时A的友元。 友元关系不能继承在继承位置再给大家详细介绍。 二、内部类 1、定义 概念如果一个类定义在另一个类的内部这个内部类就叫做内部类。 内部类是一个独立的类 它不属于外部类更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。 注意内部类就是外类部的友元类内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。 2、特性 内部类可以定义在外部类的public、protected、private都是可以的。 注意内部类可以直接访问外部类中的static成员不需要外部类的对象/类名。 sizeof(外部类)外部类和内部类没有任何关系。 我们先来看一下 ”sizeof(外部类)外部类和内部类没有任何关系“ 在代码中怎么体现的。 class A{
private:int h;
public:class B{private:int b;};
};int main()
{A aa;cout sizeof(aa) endl;return 0;
} 输出结果显示类A的对象对象只有一个int成员的大小。 在调试中也可以看到类对象aa只有一个成员变量h。 内部类B跟A是独立只是受A的类域限制。 可以通过下面代码访问到B类
A::B bb; 如果B类的作用域变为私有则不能访问到。 B天生就是A的友元。 class A{
private:int h 0;static int k;
public:class B{public:void Print(const A a){cout k endl;// OKcout a.h endl;// OK}};
};
int A::k 1;int main()
{A aa;A::B bb;bb.Print(aa);return 0;
}
通过B类成功访问A类的静态成员变量k和整型成员变量h。 这时我们就可以对使用static成员的这道题使用内部类进行修改。
求123...n_牛客题霸_牛客网 (nowcoder.com)
class Solution {class Sum {public:Sum() {_sum _i;_i;}};private:static int _sum;static int _i;public:int Sum_Solution(int n) {Sum a[n];return _sum;}
};
int Solution::_sum 0;
int Solution::_i 1;
三、匿名对象 class A
{
public:A(int a 0):_a(a){cout A(int a) endl;}~A(){cout ~A() endl;}
private:int _a;
};
class Solution {
public:int Sum_Solution(int n) {//...return n;}
};
int main()
{A aa1;// 不能这么定义对象因为编译器无法识别下面是一个函数声明还是对象定义//A aa1();// 但是我们可以这么定义匿名对象匿名对象的特点不用取名字// 但是他的生命周期只有这一行我们可以看到下一行他就会自动调用析构函数A();A aa2(2);// 匿名对象在这样场景下就很好用。Solution().Sum_Solution(10);return 0;
} 这样定义类对象可以吗 A aa1(); 不能这么定义对象因为编译器无法识别下面是一个函数声明还是对象定义。 但是我们可以这么定义匿名对象匿名对象的特点不用取名字。 A(); 但是他的生命周期只有这一行我们可以看到下一行他就会自动调用析构函数 匿名对象在这样场景下就很好用。 Solution().Sum_Solution(10); 四、拷贝对象时的一些编译器优化 在传参和传返回值的过程中一般编译器会做一些优化减少对象的拷贝这个在一些场景下还是非常有用的。 1、传值传引用返回优化对比 class A
{
public:A(int a 0):_a(a){cout A(int a) endl;}A(const A aa):_a(aa._a){cout A(const A aa) endl;}A operator(const A aa){cout A operator(const A aa) endl;if (this ! aa){_a aa._a;}return *this;}~A(){cout ~A() endl;}
private:int _a;
};void func1(A aa)
{}void func2(const A aa)
{}int main()
{A aa1 1; // 构造拷贝构造 -》 优化为直接构造func1(aa1); // 无优化不能跨表达式优化func1(2); // 构造拷贝构造 -》 优化为直接构造func1(A(3)); // 构造拷贝构造 -》 优化为直接构造cout ---------------------------------- endl;func2(aa1); // 无优化func2(2); // 无优化func2(A(3)); // 无优化return 0;
} 我们看一下main函数中的代码 A aa1 1; 这里首先调用构造函数创建一个临时对象然后调用拷贝构造函数将临时对象的内容复制到aa1。但是编译器通常会进行优化直接调用构造函数创建aa1避免了不必要的拷贝构造。func1(aa1); 这里调用函数func1参数是aa1的拷贝所以会调用拷贝构造函数。这个过程没有优化。函数func1会调用析构函数清理临时变量aa。func1(2); 和 func1(A(3)); 这两行代码都是先构造一个临时对象然后调用拷贝构造函数将临时对象的内容复制到函数参数。但是编译器会进行优化直接将临时对象作为函数参数避免了不必要的拷贝构造。 然后是func2的调用 func2(aa1); func2(2); 和 func2(A(3)); 这三行代码都是将一个对象的引用作为函数参数所以不需要调用拷贝构造函数也就没有优化的空间。 func2(aa1)引用传值不需要构造和析构。 func2(2)构造一个临时对象然后拷贝构造给aa。 func2(A(3))中 A(3) 创建了一个临时对象调用了构造函数 A(int a 0)并输出 A(int a)。 这是因为在函数调用 func2(A(3)); 中临时对象被创建即 A(3)。const A aa 表示将这个临时对象通过常引用传递给 func2 函数。在这里没有发生拷贝构造因为是通过引用传递的。 所以在 func2 函数内部没有额外的构造或拷贝构造的调用。当 func2 函数执行完毕临时对象开始析构。这时调用了析构函数 ~A()并输出 ~A()。这是因为在函数调用结束后局部变量包括通过临时对象构造的 aa会被销毁。 最后整个程序执行结束全局的 A(3) 对象也会被销毁调用析构函数 ~A()。因此总共有两次析构调用。一次是在 func2 函数内部的临时对象销毁另一次是全局的 A(3) 对象销毁。 2、匿名对象作为函数返回对象 class A
{
public:A(int a 0):_a(a){cout A(int a) endl;}A(const A aa):_a(aa._a){cout A(const A aa) endl;}A operator(const A aa){cout A operator(const A aa) endl;if (this ! aa){_a aa._a;}return *this;}~A(){cout ~A() endl;}
private:int _a;
};A func3()
{A aa;return aa;
}
A func4()
{return A();//匿名对象
}int main()
{func3();// 构造拷贝构造A aa1 func3();//构造两个拷贝构造优化为构造一个拷贝构造func4(); // 构造拷贝构造 -- 优化为构造A aa3 func4(); // 构造拷贝构造拷贝构造 -- 优化为构造return 0;
} 通过对比可以发现使用匿名对象在func4()中的好处。 在函数 func4() 中return A(); 创建了一个匿名对象并且该匿名对象直接作为函数的返回值。这样调用 func4() 将得到这个匿名对象的拷贝而不需要额外的临时对象。因此在 func4() 的调用中可以直接构造并返回这个匿名对象避免了多余的对象的创建和拷贝构造。
3、接收返回值方式对比
A func3()
{A aa;return aa;
}int main()
{A aa1 func3(); // 拷贝构造拷贝构造 -- 优化为一个拷贝构造cout **** endl;A aa2;aa2 func3(); // 声明和定义不在一行不能优化return 0;
} 总结
对象返回 接收返回值对象尽量拷贝构造方式接收不要赋值接收。函数中返回对象时尽量返回匿名对象。 函数传参 尽量使用const 传参。