C++:类的6个知识点
C++:类的6个知识点
1.初始化列表
2.类型转换
3.static成员
4.友元
5.内部类
6.匿名对象
1.初始化列表
- 构造函数初始化一种是函数体内赋值,另一种是初始化列表。使用方式:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个成员变量后面跟一个放在括号中的初始值或表达式。
- 每个成员变量在初始化列表中只能出现一次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。
- 引用成员变量、const成员变量、无默认构造的类类型变量,必须在初始化列表中初始化。
#include<iostream>
using namespace std;
class Time //无默认构造的类
{
public:Time(int hour):_hour(hour){cout<<"Time()"<<endl;}
private:int _hour;
};
class Date
{
public:Date(int& x,int year=1,int month=1,int day=1):_year(year),_month(month),_day(day),_t(12),_ref(x),_n(1){}
private: //变量声明int _year;int _month;int _day;Time _t; //无默认构造int& _ref;//引用const int _n;//const
};
int main()
{int x=0;Date d(x);return 0;
}
- C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给未显示在初始化列表初始化的成员使用。
- 尽量使用初始化列表初始化,如果这个成员在声明位置给了缺省值,初始化列表用缺省值初始化。未显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,自定义类型成员会调用默认构造,若无默认构造则会编译错误。
- 初始化列表中,按照成员变量在类中声明顺序初始化。
- 总结:每个构造函数都有初始化列表,每个成员变量都要走初始化列表初始化。
#include<iostream>
using namespace std;
class Time
{
public:Time(int hour):_hour(hour){cout<<"Time()"<<endl;}
private:int _hour;
};
class Date
{
public:Date():_month(2){}
private: //变量声明int _year=1;int _month=1;int _day=1;Time _t=1; //无默认构造const int _n=1;//constint* _p=(int*)malloc(12);//表达式
};
int main()
{Date d;return 0;
}
2.类型转换
- 内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。
- 构造函数前面加explicit就不再支持隐式类型转换。
- 类类型对象之间也可以隐式转换,需要相应构造函数支持。
#include<iostream>
using namespace std;
class A
{
public:A(int a1):_a1(a1){}A(int a1,int a2):_a1(a1),_a2(a2){}int Get()const{return _a1+_a2;}
private:int _a1=1;int _a2=1;
};
class B
{
public:B(const A& a):_b(a.Get()){}
private:int _b=0;
};
int main()
{A aa1=1;//内置类型隐式转换为类类型const A& aa2=1;//1先转换为A类的临时对象,再将临时对象拷贝给aa2,临时对象有常性,所以constA aa3={2,2};B b=aa3;//A类隐式转换为B类const B& rb=aa3;return 0;
}
例如,对于aa1:
3.static成员
- 用static修饰的成员变量,即静态成员变量,一定要在类外进行初始化。
- 静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。
- 用static修饰的成员函数,即静态成员函数,静态成员函数无this指针。
- 静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。
- 非静态的成员函数,可以访问静态成员变量和静态成员函数。
- 访问静态成员:类名::静态成员或对象.静态成员 。
- 静态成员也受public、protected、private限制。
- 静态成员变量不能在声明位置给缺省值初始化,因为静态成员变量不属于某个对象,不走构造函数初始化列表。
例如:求1+2+3+…+n,不使用乘除,if…else,switch,while,do…while,for,三目运算符 ?: 。
#include<iostream>
using namespace std;
class Sum
{
public:Sum(){_ret+=_i;_i++;}static int GetRet(){return _ret;}
private:static int _i;static int _ret;
};
int Sum::_i=1;
int Sum::_ret=0;
class Solution
{
public:int Sum_Solution(int n){Sum arr[n];//变长数组,创建n个Sum类的对象,每创建一次就调用一次构造函数Sum()return Sum::GetRet();//使用静态成员函数}
};
int main()
{Solution s;s.Sum_Solution(10);//取n=10return 0;
}
由于每创建一次就调用一次构造函数Sum(),_i从1开始变化到n,所以_ret就加1,加2,加3……一直加到n 。
vs2022中不能使用变长数组,所以在函数int Sum_Solution(int n)中去掉参数n进行调试。
- 注意:如果GetRet函数去掉static,就要在函数Sum_Soultion中返回arr[9].GetRet(),才能得到正确答案。
4.友元
- 友元分为:友元函数和友元类,在函数声明或者类声明的前面加friend,并且把友元声明放到一个类的里。
- 外部友元函数可访问类的私有和保护成员,友元函数仅仅是一种声明,他不是类的成员函数。
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
- 一个函数可以是多个类的友元函数。
- 友元类中的成员函数都可以是另一个类的友元函数。
- 友元类的关系是单向的,A类是B类的友元,但是B类不是A类的友元。
- 友元类关系不能传递,A是B的友元, B是C的友元,但是A不是C的友元。
- 友元会增加耦合度,破坏封装,不宜多用。
class B;//前置声明,否则A的友元函数声明中,编译器不认识B
class A
{friend void func(const A& aa,const B& bb);//友元声明
private://......
};
class B
{friend void func(const A& aa,const B& bb);//友元声明
private://......
};
void func(const A& aa,const B& bb)
{//......
}
5.内部类
- 即一个类定义在另一个类的内部,受外部类域和访问限定符限制,外部类定义的对象中不包含内部类 。
- 内部类默认是外部类的友元类。
- 若A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类。如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方无法使用。
例如求1+2+……+n,也可以这样写:
#include<iostream>
using namespace std;
class Solution
{class Sum{public:Sum(){_ret+=_i;_i++;}};static int _i;static int _ret;
public:int Sum_Solution(int n){Sum arr[n];return _ret;}
};
int Solution::_i=1;
int Solution::_ret=0;
int main()
{Solution s;s.Sum_Solution(10);return 0;
}
6.匿名对象
- 即 类型(实参) ,有名对象即 类型 对象名(实参) 。
- 匿名对象生命周期只在当前一行 。
#include<iostream>
using namespace std;
class A
{
public:A(int a=0):_a(a){}~A(){}
private:int _a;
};
class Solution
{
public:int Sum_Solution(int n){return n;}
};
int main()
{A aa1;A(); //匿名对象A(1);//匿名对象Solution().Sum_Solution(10);//匿名对象return 0;
}
拙作一篇,望诸位同道不吝斧正。