开江建设局网站,企业如何做好网络营销,做章的网站,网站建设佰首选金手指二六目录
前言#xff1a;
1. 构造函数-初始化列表
1.1 初始化列表出现原因
1.2 初始化列表写法
2. explicit关键字
2.1 explict的出现
2.2 explict的写法
3. static成员
4. 友元
4.1 友元函数
4.2 友元类
5. 内部类和匿名对象
5.1 内部类
5.2 匿名对象 前言
1. 构造函数-初始化列表
1.1 初始化列表出现原因
1.2 初始化列表写法
2. explicit关键字
2.1 explict的出现
2.2 explict的写法
3. static成员
4. 友元
4.1 友元函数
4.2 友元类
5. 内部类和匿名对象
5.1 内部类
5.2 匿名对象 前言 本篇讲解的内容是类和对象的补充知识也就是类和对象当中平时不太注意但是有比较重要的知识。其中包括构造函数中的初始化列表、static成员、友元、内部类、匿名对象以及部分小知识。如果小伙伴们有需要请向下查看咯。
1. 构造函数-初始化列表
1.1 初始化列表出现原因 朋友们还记得我上篇讲的当我们在实例化一个对象的时候编辑器会干嘛会调用类里的构造函数对类里面的成员变量作合适的初始化。如下
class Date
{
public:Date(int year, int month, int day){_year year;_month month;_day day;}
private:int _year;int _month;int _day;
}; 但是朋友们有没有想过一个问题那就是虽然我们在实例化的时候传入了一个参数但是那真的算是初始化吗可能有的小伙伴没有理解我的话。那我换一个说法我们传入一个值用于初始化并且将这个值赋值给了一个变量那么这一步算是初始化吗 答案是否这一个过程并不能说是初始化我可以举一个例子 int a 0; //初始化a为0 int b; //创建了一个变量b b 0; //赋值b为0 大家可以看出这两者的区别了吗我相信是能的那么再回到我们的构造函数这个时候我们再看它他是初始化还是赋值操作相信大家心里有数了吧。 不过话又说回来这个初始化和赋值有什么影响吗这么看起来好像没有区别啊确实如果是这种类型的变量赋值和直接初始化没有区别但是大家可能忘了某些东西引用、const成员变量。想到了吗 int c; //引用不初始化 const int d; // const成员变量不初始化 d 10; //const成员变量重新赋值 这几个写法很明显是错误的相信大家也能够分辨出来那么我们将这两个类型写在类里此时我们的写法还正确吗肯定是错误的。 所以C祖师爷基于这个点为我们的构造函数多添加了一个功能——初始化列表。
1.2 初始化列表写法 先看一下代码是如何写的
class A
{
public:A(int a) :_a(a) //初始化列表{}
private:int _a;
};
class B
{
public:B(int a, int ref):_aobj(a) //初始化列表, _ref(ref), _n(10){}
private:A _aobj; // 没有默认构造函数int _ref; // 引用const int _n; // const
}; 大家看看我定义的两个类有没有发现它里面的构造函数多了个东西那就是以冒号开始用逗号分隔每一个变量每个变量加上括号和初始值。 用上述的B类构造函数为例子 :_aobj(a) , _ref(ref) , _n(10) 这就是初始化列表写法当然如果我们初始化有特殊的需求还是可以将功能写在初始化列表下方的花括号中例如条件判断、申请堆上的空间之类的。 编辑器有一个对于初始化列表的规定那就是不管我们写不写它都会自己去运行一遍不过它却不会对这些变量做任何的处理只有我们写了他才会为其赋初值。 还有一点需要我们注意请先看下方代码并计算_a1和_a2的初始值。
class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout _a1 _a2 endl;}
private:int _a2;int _a1;
};int main() {A aa(1);aa.Print();
}
选项 A. 输出1 1 B.程序崩溃 C.编译不通过 D.输出1 随机值 答案是D与大家计算的是否有出入了相信多数朋友会选择a吧因为我就是其中之一。 其实初始化列表的初始化顺序不是我们眼睛看到的那样它的初始化顺序是变量定义的顺序看下代码
private:int _a2;int _a1; 也就是说_a2先用_a1的初始值也就是随机值赋值了然后_a1再通过1赋值这也就造成了我们之前产生的小bug。
2. explicit关键字
2.1 explict的出现 在讲解这个关键字的之前我想问一个问题大家知道下面的两种实例化对象的方式有什么不同吗不是说写法。 Date d1(2023,2,17); Date d2 {2023,2,17}; 这样问估计有些难为人我就直接讲了第一种会直接用这些初始值通过构造函数实例化 但是第二种不同第二种通过这些初始值调用构造函数实例化之后然后通过赋值的方式将其转换给d2。大家看出区别了吗也就是说第一种实例化只有构造函数参与但是第二种不仅有构造函数还有拷贝构造参与。不过对于现在的编辑器来说已经被优化了二者都只有构造函数参与如果按照刚出现的那段时间用第二种在构造一个对象的时候产生两个变量大小的空间这务必会影响我们程序运行的效率。 那我问大家为什么会出现这个过程呢先看下方的代码 int a 10; double b (double)a; 问我们将a强制类型转换成double类型int a是否还存在。 答案是当然存在强制类型转换只会新开辟一个临时空间存储然后赋值给b其中a的参与只有提供10这个数的价值。通过等效的理解方式大家再去看上面的Date d2 {2023,2,17};是不是瞬间就明白了只不过该操作是隐式类型转换。 explict的出现就是为了禁止这种强制类型转换的。
2.2 explict的写法
class Date
{
public:explicit Date(int year):_year(year){}Date operator(const Date d){if (this ! d){_year d._year;_month d._month;_day d._day;}return *this;}
private:int _year;int _month;int _day;
};
void Test()
{Date d1(2022);d1 2023; //错误} 大家可以看到如果我们不添加explictd1 2023这种写法是正确的但是大家可以赋值这段代码进入你的编辑器看一看这个操作是否还正确 所以explict的功能是用explicit修饰构造函数将会禁止构造函数的隐式转换。
3. static成员 对于static关键字想必大家还是很了解的但是当他出现在类里面还清楚吗 我们都知道static描述的变量这个变量的生命周期会到程序结束才会停止而不是局限于函数结束就被销毁。这个特性static也延续到类当中当用static去修饰类里面的变量那么这个变量的生命周期就被提升并且它并不单独属于某个实例化的类对象而是属于整个类。 大伙看到这估计有些疑问他既然写在了类里面又不属于这个类的对象那么它在哪答案是它被存在了静态区。也就是计算类的大小也不会将他算入其中。
class Date
{
public:static int a;
private:int _year;int _month;int _day;
};int main()
{Date d1;cout sizeof(d1) endl;return 0;
} 正常来说这个类的大小应该是16对吧因为又4个整型变量嘛但是其中有一个变量是static修饰的所以它的大小不计入其中。 因为这个static修饰的变量不属于某一个变量所以说没有任何一个对象想能对其进行初始化只能有一个操作那就是类外定义。 int Date::a 0; 至于为什么要这样做想一想要是每一个对象都能够对static初始化那么static修饰这个变量有什么意义呢 并且static不只是可以修饰变量还可以修饰成员函数将这个成员函数变为静态成员函数。
以下几个特性需要大家知道 1. 静态成员为所有类对象所共享不属于某个具体的对象存放在静态区 2. 静态成员变量必须在类外定义定义时不添加static关键字类中只是声明 3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问 4. 静态成员函数没有隐藏的this指针不能访问任何非静态成员 5. 静态成员也是类的成员受public、protected、private 访问限定符的限制 知道了这些特点下面两个问题相信大家也能够回答了。 1. 静态成员函数可以调用非静态成员函数吗 2. 非静态成员函数可以调用类的静态成员函数吗
4. 友元 友元提供了一种突破封装的方式有时提供了便利。但是友元会增加耦合度破坏了封装所以友元不宜多用。 友元分为友元函数和友元类
4.1 友元函数 一般来说友元用到的场景不会太多但是有一个东西如果不用友元那么它的操作方式会变得十分奇怪那就是重定义和这两个操作符。
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}ostream operator(ostream _cout){_cout _year - _month - _day endl;return _cout;}
private:int _year;int _month;int _day;
};int main()
{Date d1(1, 2, 3);d1 cout;//cout d1; //错误return 0;
} d1 cout; - d1.operator(d1, cout); 不符合常规调用因为成员函数第一个参数一定是隐藏的this所以d1必须放在的左侧。 还记得我上一篇讲的吗因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象才能正常使用。所以要将operator重载成全局函数。但又会导致类外没办法访问成员此时就需要友元来解决。operator同理。 这个时候友元函数的出现就显得十分重要了
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;
} 可以看到我们的友元函数的写法就是将函数定义在类外面然后在类里面写下声明并在前面添加上friend关键字。声明的位置可以是任意位置但是我们习惯将其写在类最上方而且友元函数突破了public、protected、private的限制。也就是友元函数能够直接访问类里面的私有成员。 friend ostream operator(ostream _cout, const Date d); friend istream operator(istream _cin, Date d); 特性 友元函数可访问类的私有和保护成员但不是类的成员函数 友元函数不能用const修饰 友元函数可以在类定义的任何地方声明不受类访问限定符限制 一个函数可以是多个类的友元函数 友元函数的调用与普通函数的调用原理相同 4.2 友元类
特性 1. 友元类的所有成员函数都可以是另一个类的友元函数都可以访问另一个类中的非公有成员。 2. 友元关系是单向的不具有交换性。比如上述Time类和Date类在Time类中声明Date类为其友元类那么可以在Date类中直接访问Time类的私有成员变量但想在Time类中访问Date类中私有的成员变量则不行。 3. 友元关系不能传递如果C是B的友元 B是A的友元则不能说明C时A的友元。 4. 友元关系不能继承在继承位置再给大家详细介绍。 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){// 直接访问时间类私有的成员变量_t._hour hour;_t._minute minute;_t._second second;}
private:int _year;int _month;int _day;Time _t;
};
5. 内部类和匿名对象
5.1 内部类
概念 1. 如果一个类定义在另一个类的内部这个内部类就叫做内部类。内部类是一个独立的类 它不属于外部类更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。 2. 注意内部类就是外部类的友元类参见友元类的定义内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。 特性 1. 内部类可以定义在外部类的public、protected、private都是可以的。 2. 注意内部类可以直接访问外部类中的static成员不需要外部类的对象/类名。 3. sizeof(外部类)外部类和内部类没有任何关系。 class A
{
private:static int k;int h;
public:class B // B天生就是A的友元{public:void foo(const A a){cout k endl;//OKcout a.h endl;//OK}};
};
int A::k 1;
int main()
{A::B b;b.foo(A());return 0;
}
5.2 匿名对象
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; } 以上就是我对类于对象的全部补充啦还请大家多多支持咯