C++基础
目录
学习内容:
1. 匿名对象
2. 友元
2.1 友元的引入
2.2 友元函数
2.3 友元类
2.4 友元的总结
3. 常成员(const)
3.1 常成员的引入
3.2 常成员函数
3.3 常对象
3.4 mutable关键字
3.5 常函数
3.6 关于C/C++中const的使用 (面试题)
学习内容:
1. 匿名对象
1> 所谓匿名对象,就是没有名字的对象,生命周期只在当前语句内,所以可以理解成时一个将亡值
2> 定义格式:直接调用类的构造函数
3> 使用场景
1、使用匿名对象给一个新定义的对象进行初始化
2、使用匿名对象给数组进行初始化
3、使用匿名对象作为函数参数传递
#include <iostream>
using namespace std;
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;}
};//自定义全局函数
void fun(Stu s)
{s.show();
}int main()
{Stu s1("yyx", 18); //调用有参构造s1.show();cout<<"***********************************"<<endl;Stu("lisi", 20); //定义一个匿名对象cout<<"***********************************"<<endl;//匿名对象使用方式1:给新的对象进行初始化工作Stu s2 = Stu("wangwu", 30); //依赖移动构造cout<<"***********************************"<<endl;//匿名对象使用方式2:给对象数组进行初始化Stu s[3] = {Stu("zhaoliu", 30), Stu("sunqi", 30), Stu("zhouri", 30)};cout<<"***********************************"<<endl;//匿名对象使用方式3:作为函数的形参进行传递fun(Stu("wukong", 500));cout<<"***********************************"<<endl;return 0;
}
2. 友元
2.1 友元的引入
由于类的封装性较强,对类外的所有数据都屏蔽了信息,尤其是私有成员。但是,程序员有时候又想要在外界某个函数或某个类中访问其他类的私有成员,此时就可以引入友元,在某个类中,将外部的信息设置成友元,那么,该友元就会无条件访问该类中的所有权限下的成员。
2.2 友元函数
1> 全局函数作为友元函数
生命类外的一个全局函数当作该类的友元函数,那么该函数就能无条件的访问该类中的任意权限下的成员
生命格式: friend 函数头;
#include <iostream>using namespace std;
class Stu; //对类的前置声明
void fun(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);
};//定义一个全局函数
void fun(Stu s)
{s.show(); //在类外可以正常调用类中的公共权限下的成员cout<<"name = "<<s.name<<endl; //友元函数可以访问对应类中的所有权限下的成员cout<<"age = "<<s.age<<endl; //友元函数可以访问对应类中的所有权限下的成员
}int main()
{Stu s1("zhangsan", 18);fun(s1);return 0;
}
2.3 友元类
声明一个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("yyx", "C++");t1.display(s1); //调用成员函数展示其他对象内容return 0;
}
2.4 友元的总结
1> 不到万不得已的情况下,不要使用友元,因为友元的出现使得封装称为虚谈,友元破坏了类的封装性
2> 友元不具有传递性:A是B的朋友,B不一定是A的朋友
3> 友元不具有传递性:A是B的朋友,B是C的朋友,A不一定是C的朋友
4> 友元不具有继承性:父类的朋友,不一定是子类的朋友
5> 必须使用友元的情况:插入和提取运算符重载时,只能使用友元函数来解决
3. 常成员(const)
3.1 常成员的引入
在封装成员函数时,有时该成员函数,仅仅对成员变量有只读权限,不需要进行更改,那么此时就需要对成员函数进行进一步操作,达到保护成员变量的功能。
有时,在传递某个对象时,对方仅仅只是对该对象有只读权限,也需要使用相关操作,进一步保护成员变量
此时就可以引入常成员,保护成员函数和成员变量、常对象
3.2 常成员函数
1> 定义格式:定义成员函数时,在括号后加关键字 const
返回值类型 函数名(形参列表) const
2> 作用:在常成员函数中,是不允许更改成员变量的值
3> 类中常成员函数与同名的非 常成员函数构成重载关系,原因是,形参this的类型不同
非常成员函数中形参this:类名 * const this;
常成员函数中的形参this: 类名 const * const this;
4> 非 常对象,优先调用非常成员函数,如果没有该非常成员函数,那么就会调用同名的常成员函数
3.3 常对象
1> 定义格式:const 类名 对象名;
2> 常对象只能调用常成员函数,不能调用非常成员函数
3.4 mutable关键字
1> 使用该关键字修饰的成员变量,可以解除常属性,即使在常成员函数中也能更改该变量的值
#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;
}
3.5 常函数
1> 定格式:const 返回值类型 函数名(形参列表){函数体内容}
2> 功能:常函数是保护函数的返回值不被修改的
#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;
}
3.6 关于C/C++中const的使用 (面试题)
1> const修饰普通变量,表示定义的是一个常变量,该变量只可读不可写,定义时必须初始化
2> const修饰指针变量时,如果放在*前表示,指针所执行空间中的内容可读不可写
如果放在*后,表示指针的值可读不可写,也就是指针指向不能改变
如果两边都加,表示都不可以更改,都是可读不可写,包含指向和值
3> const修饰函数的形参,表示传过来的数据,可读不可写
4> const修饰函数返回值时,表示保护函数的返回结果不被修改(指针函数和引用函数)
5> const修饰常成员函数,放到成员函数最后面,表示保护成员变量不被修改