1.const
(1).const成员数据
const修饰词,可以修饰成员数据,也可以修饰成员函数。修饰成员数据时,表示成员数据不能更改。且成员函数只能通过初始化列表来赋值(或者在定义数据成员时就给它赋初始值)。
class MM{
public:
MM(){string name,int age}:name(name),age(age){
}
void print(){
//age=22; 错误 一旦通过初始化参数列表之后就不能再修改
}
protected:
const int age = 10;
string name;
}
(2)const成员函数
const 成员函数可以使用类中的所有成员变量,但是不能修改它们的值,这种措施主要还是为了保护数据而设置的。const 成员函数也称为常成员函数。
常成员函数可以与成员函数的函数名相同,调用函数方法时,优先调用普通成员函数,普通函数找不到,才会调用常成员函数。
class MM{
public:
MM(){string name,int age}:name(name),age(age){
}
void print(){
//age=22; 错误 一旦通过初始化参数列表之后就不能再修改
}
//常成员函数
void print() const{
//string name = "xx" 错误写法
cout<<name<<endl;
}
protected:
const int age = 10;
string name;
}
(3)const常对象
和普通定义对象一样,只不过普通定义对象前面加个cosnt修饰。但和普通对象不同,常对象只能调用常成员函数。无法调用普通成员函数,普通对象既能调用普通成员函数,又能调用常成员函数。但是,当常成员函数和普通成员函数重名时,普通对象无法强制调用常成员函数。
const MM m1; //定义一个无参常对象
小结:
+ const数据成员
+ const类型变量是不可以修改,只读模式
+ 必须采用初始化参数列表方式进行初始化
+ const成员函数
+ 写法上, const写在函数后面
+ 常成员函数是不能够修改数据成员,只读数据成员
+ 常成员函数可以与普通函数同时存在
+ 普通函数和常成员函数相同时,普通对象优先调用普通函数
+ 普通对象可以调用常成员函数
+ const对象: const修饰的对象
+ 常对象只能调用常成员函数
2.static
static成员是不属于对象,是属于类的,意味着是所有对象共有的,调用可以不需要对象,当然你可以用对象调用(前提是在public下定义的静态数据成员),即静态数据成员仍然受权限限定。
(1).static成员数据
static定义的成员数据只能在类外初始化(定义的时候赋值也是错误的),类外初始化的时候不需要加static,但需要加上类名限定。
class MM{
public:
//错误写法,静态数据成员并不能通过初始化参数列表来初始化
//MM(string name,int age):name(name),age(age){};
protected:
static string name;
static int age;
}
//静态成员数据必须在类外初始化,不能在类内初始化
int MM::age=20;
string MM::name="xxx";
让我们来看一个实例,更好的理解静态成员变量是类中公有的这个概念。
class MM{
public:
//不能使用初始化参数列表的方式来初始化static类型的变量
MM(string name=" ") :name(name){
num++;
}
//静态成员函数
static void printMM();
protected:
string name;
public:
static int num;
};
int MM::num=0;
int main(){
MM m1("xx");
cout<<MM::num<<endl; //num=1
MM m2[3];
cout<<MM::num<<endl; //num=4
MM* m3=new MM("zz");
cout<<m3.num<<endl; //num=5
return 0;
}
代码中,MM类中static数据成员num 会随着构造函数的调用而增加。每调用一次构造函数,就会num+1,到最后调用了5次构造函数 num=5。
(2).static成员函数
在成员函数前加上一个static修饰即可。和静态成员数据一样,在类外实现的时候不需要static,只要加上类名限定就行。
class MM{
public:
MM(string name=" ") :name(name){
num++;
}
//静态成员函数
static void printMM();
protected:
string name;
public:
static int num;
};
//类外实现静态成员函数 需要类名限定
void MM::printMM(){
cout<<"静态成员函数"<<endl;
}
但是需要注意的是静态成员函数调用非静态成员数据时要指定对象。很容易理解(静态成员函数是属于类的,是每个对象共有的,而对于非静态成员数据来说,每个对象对应的值都不相同。不指定对象,静态成员函数不知道去访问哪个。)
3.友元
友元只是提供一个场所,赋予对象具有打破类的权限定(无视权限)。函数前置加friend,需要注意:友元函数不属于类(不是成员函数),不可以直接访问类中的成员数据。但是在友元函数中,对象可以直接访问类中的成员数据,不受权限限定。
(1).普通友元函数
成员函数在调用时会隐式地增加 this 指针,指向调用它的对象,从而使用该对象的成员;而 show() 是非成员函数,没有 this 指针,编译器不知道使用哪个对象的成员,要想明确这一点,就必须通过参数传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。
class MM{
public:
MM(){string name,int age}:name(name),age(age){
}
//友元函数
friend void print(MM& mm){
//下面是错误写法
cout<<name<<" "<<age<<endl; 错误写法 友元函数不属于类,不能直接访问成员数据
//需要传入对象或者对象的引用
cout<<mm.name<<" "<<mm.age<<endl;
}
protected:
int age = 10;
string name;
}
int main(){
MM m(" ",22);
//调用友元函数直接访问成员数据
print(mm);
return 0;
}
友元函数不属于类,所以友元函数在类中定义类外实现时,不需要加类名限定,也不需要加friend修饰。
因为友元函数不属于类,所以不能直接访问类中的非静态成员变量!!!
因为友元函数不属于类,所以不能直接访问类中的非静态成员变量!!!
因为友元函数不属于类,所以不能直接访问类中的非静态成员变量!!!
(2).以另一个类的成员函数为友元函数(很容易出错)
本质还是友元函数。
例:以B类中的成员函数为A的友元函数:首先要先写B类,并声明成员函数。然后写A类,并声明友元函数,接着在类外实现B类的成员函数。这个顺序不能修改。
//以B类的成员函数为A类的友元函数示例
class B{
public:
void printA();
protected:
}
class A{
public:
//申明友元函数
friend void B::printA();
protected:
string name="A";
}
//成员函数实现
void B::printA(){
//A类中的友元函数,在友元函数中,可以突破类的限制,直接访问A类的保护属性
A a;
cout << a.name << endl;
}
(3).友元类
在一个类中申明另一个类为友元类,则另一个类就可以访问
#include<iostream>
#include<string>
using namespace std;
//前向申明一下
class B;
class A{
public:
A(string name) :name(name){ }
void print();
void printData(B& b);
protected:
string name = "A";
};
class B{
public:
B(string name = "B") :name(name){ }
//申明B是A的友元类
friend class A; //A中所有成员函数都是B的友元函数
protected:
string name = "B";
};
void A::print(){
B b;
cout << b.name << endl;
}
void A::printData(B& b){
cout << b.name << endl;
}
int main(){
B b;
A a("A");
a.print();
a.printData(b);
return 0;
}
上述声明了A类是B类的友元,则A类中所有的成员函数都是B类的友元函数,都可以无视类的权限限定。(为了防止出现未定义的问题 ,成员函数最好在类外定义,并且加上类的前项申明)
4.this指针和explicit
explicit是用来修饰构造函数的,作用是创建对象时限制隐式转换(限制等号赋值的方式来创建对象)。
class MM{
public:
//explicit 限制隐式转换
explicit MM(int age) : age(age){
}
void printThis(){
cout << this << endl;
}
protected:
int age;
};
int main(){
//隐式转换,不加explicit时可以创建对象。
//MM m=20;
//构造函数加了explicit修饰 就不能隐式转换
MM m(20);
return 0;
}
this指针可以避免非静态成员函数的形参名和数据成员名相同时引发的问题。
this指针也可以返回对象的本身,没有this指针,没法写出返回对象的本身。
class GG
{
public:
GG(string name, int age) :name(name), age(age) {}
//普通函数不存在初始化参数列表
void initData(string name, int age)
{
//类名限定 帮助计算机去识别
GG::name = name;
//或者用this指针
this->age = age;
}
//用this代替对象输出成员数据
void print()
{
cout << this->name << " " << this->age << endl;
}
//输出this指针指向的地址,和对象的地址是一样的
void printThis()
{
cout << this << endl;
}
//使用this指针返回对象
GG& returnGG()
{
return *this;
}
static void printStatic()
{
GG gg("this", 19);
cout << gg.name << "\t" << gg.age << endl;
}
protected:
string name;
int age;
};