一、匿名对象
没有名字的对象,生命周期只在当前语句内
1、定义格式:直接调用类的构造函数
#include <iostream>
using namespace std;
class Stu
{
private:
string name;
int age;
public:
Stu(){}//无参构造
Stu(string n ,int a):name(n),age(a){}//有参构造
~Stu(){}//析构函数
void show()
{
cout<<name<<" "<<age<<endl;
}
};
int main()
{
Stu s1("zzz",18);
s1.show();
Stu(); //定义匿名对象
return 0;
}
2、匿名对象使用场景
#include <iostream>
using namespace std;
class Stu
{
private:
string name;
int age;
public:
Stu(){}//无参构造
Stu(string n ,int a):name(n),age(a){}//有参构造
~Stu(){}//析构函数
Stu(Stu &other):name(other.name),age(other.age){}//拷贝构造
Stu(Stu &&other):name(other.name),age(other.age){}//移动构造
void show()
{
cout<<name<<" "<<age<<endl;
}
};
void fun(Stu s)
{
s.show();
}
int main()
{
Stu s1("zzz",18);
s1.show();
Stu(); //定义匿名对象
//匿名对象使用场景1:给新的对象初始化
Stu s2 = Stu("sss",30);//依赖移动构造
//匿名对象使用场景2:给新的对象组初始化
Stu s[3] = {Stu("sss",30),Stu("sss",30),Stu("sss",30)};
//匿名对象使用场景3:作为函数的形参传递
fun(Stu("yyy",500));
return 0;
}
二、友元
类的封装性较强,对类外的所有数据都屏蔽了信息,类外向访问类内非公共成员需要使用类提供的公共接口。
如果,在类内将某个外部设置成友元,那么,该友元就会无条件访问该类中的所有权限下的成员。
由于,友元破坏了内的封装性,使类失去了意义,所以一般不使用
1、友元函数
1)全局函数作为友元函数
#include <iostream>
using namespace std;
class Dog ;
class Cat ;
class Cat
{
private:
int count;
int weight;
public:
Cat(){}
Cat(int c,int w):count(c),weight(w){}
friend int sum_count(Cat c,Dog d);
friend int sum_weight(Cat c,Dog d);
};
class Dog
{
private:
int count;
int weight;
public:
Dog(){}
Dog(int c,int w):count(c),weight(w){}
friend int sum_count(Cat c,Dog d);
friend int sum_weight(Cat c,Dog d);
};
int sum_count(Cat c,Dog d)
{
return c.count+d.count;
}
int sum_weight(Cat c,Dog d)
{
return c.weight+d.weight;
}
int main()
{
Cat c(8,9);
Dog d(10,5);
cout<<sum_count(c,d)<<" "<<sum_weight(c,d)<<endl;
return 0;
}
2)其他类的成员作为友元函数
声明方法与全局函数一致
如果类中的某个函数,在其他类中设置成友元,那么该函数必须是类内声明,类外定义
2、友元类
声明一个A类为B类的友元类,则B允许A访问其所有权限下的成员包括私有成员
声明格式:friend 类名;
#include <iostream>
using namespace std;
class Stu; //对类的前置声明
void fun(Stu s);
class Teacher; //将老师类前置声明
//定义老师类
class Teacher
{
private:
string name;
string subject; //课程
public:
Teacher() {}
Teacher(string n, string s):name(n), subject(s) {}
~Teacher(){}
//定义成员函数
void display(Stu s); //只能类内声明,类外定义
};
class Stu
{
private:
string name;
int age;
public:
Stu() {cout<<"无参构造"<<endl;} //无参构造
Stu(string n, int a):name(n),age(a){cout<<"有参构造"<<endl;} //有参构造
~Stu(){cout<<"析构函数"<<endl;} //析构函数
Stu(Stu &other):name(other.name), age(other.age){cout<<"拷贝构造"<<endl;} //拷贝构造
Stu(Stu &&other):name(other.name), age(other.age){cout<<"移动构造"<<endl;} //移动构造
void show()
{
cout<<"name = "<<name<<" age = "<<age<<endl;
}
//声明一个全局函数作为友元函数
friend void fun(Stu s);
//声明其他类中的某个成员函数作为友元函数
//friend void Teacher::display(Stu s);
//声明老师类为友元类
friend class Teacher;
};
//将teacher类中国的函数类外定义
void Teacher::display(Stu s)
{
cout<<"stu::name = "<<s.name<<endl; //该函数被stu类声明为友元函数
cout<<"stu::age = "<<s.age<<endl;
}
//定义一个全局函数
void fun(Stu s)
{
s.show(); //在类外可以正常调用类中的公共权限下的成员
cout<<"name = "<<s.name<<endl; //友元函数可以访问对应类中的所有权限下的成员
cout<<"age = "<<s.age<<endl; //友元函数可以访问对应类中的所有权限下的成员
}
int main()
{
Stu s1("zhangsan", 18);
fun(s1);
Teacher t1("zhangpp", "C++");
t1.display(s1); //调用成员函数展示其他对象内容
return 0;
}
3、友元总结
1)不到万不得已的情况下,不要使用友元,因为友元的出现使得封装称为虚谈,友元破坏了类的封装性
2)友元不具有传递性:A是B的朋友,B不一定是A的朋友
3)友元不具有传递性:A是B的朋友,B是C的朋友,A不一定是C的朋友
4)友元不具有继承性:父类的朋友,不一定是子类的朋友
5)必须使用友元的情况:插入和提取运算符重载时,只能使用友元函数来解决
三、常成员 (const)
对不需要修改的成员进行保护,防止内容被更改
定义格式:定义成员函数时,在括号后加关键字 const
返回值类型 函数名(形参列表) const
1、常成员函数
1)定义格式:定义成员函数时,在括号后加关键字 const
返回值类型 函数名(形参列表) const
2)作用:在常成员函数中,是不允许更改成员变量的值
3)类中常成员函数与同名的非 常成员函数构成重载关系,原因是,形参this的类型不同
非常成员函数中形参this:类名 * const this;
常成员函数中的形参this: 类名 const * const this;
4)非 常对象,优先调用非常成员函数,如果没有该非常成员函数,那么就会调用同名的常成员函数
2、常对象
1)定义格式:const 类名 对象名;
2)常对象只能调用常成员函数,不能调用非常成员函数
3、mutable 关键字
使用该关键字修饰的成员变量,可以解除常属性,即使在常成员函数中也能更改该变量的值
#include <iostream>
using namespace std;
class Stu
{
private:
mutable string name; //使用mutable修饰的成员变量,运行在常成员函数中被修改
int age;
public:
Stu() {cout<<"无参构造"<<endl;} //无参构造
Stu(string n, int a):name(n),age(a){cout<<"有参构造"<<endl;} //有参构造
~Stu(){cout<<"析构函数"<<endl;} //析构函数
Stu(Stu &other):name(other.name), age(other.age){cout<<"拷贝构造"<<endl;} //拷贝构造
Stu(Stu &&other):name(other.name), age(other.age){cout<<"移动构造"<<endl;} //移动构造
//定义成员函数
void show()const // 类名 const * const this;
{
this->name = "libai"; //在常成员函数中,不能修改成员变量的值,但是可以修改由mutable修饰的成员变量
//this->age = 100; //普通的成员变量不能在常成员函数中被修改
cout<<"name = "<<name<<" age = "<<age<<endl;
}
void show() // 类名 * const this;
{
//name = "libai"; //在常成员函数中,不能修改成员变量的值
this->age = 100;
cout<<"name = "<<name<<" age = "<<age<<endl;
}
};
int main()
{
Stu s1("zhangsan", 18); //定义一个非常对象
s1.show(); //非常对象,优先调用非常成员函数
const Stu s2("lisi", 20); //定义一个常对象
s2.show(); //常对象只能调用常成员函数
return 0;
}
4、常函数
保护函数的返回值不被修改
定义格式:const 返回值类型 函数名(形参列表){函数体内容}
#include <iostream>
using namespace std;
//引用函数,const修饰的函数称为常函数
const int &fun()
{
static int num = 520;
return num;
}
int main()
{
cout<<"fun() = "<<fun()<<endl; //对函数返回值进行读操作
//fun() = 1314; //对函数返回值进行写操作,但是const修饰的函数,对函数返回结果进行保护
cout<<"fun() = "<<fun()<<endl;
return 0;
}
四、C/C++中const的使用
1、const修饰普通变量,表示定义的是一个常变量,该变量只可读不可写,定义时必须初始化
2、const修饰指针变量时,如果放在*前表示,指针所执行空间中的内容可读不可写
如果放在*后,表示指针的值可读不可写,也就是指针指向不能改变
如果两边都加,表示都不可以更改,都是可读不可写,包含指向和值
3、const修饰函数的形参,表示传过来的数据,可读不可写
4、const修饰函数返回值时,表示保护函数的返回结果不被修改(指针函数和引用函数)
5、const修饰常成员函数,放到成员函数最后面,表示保护成员变量不被修改