结构体在C和C++的不同
1.在c++中,用结构体定义变量不需要加struct,而c中不行。
2.在C++中,可以定义空结构体,大小为1,而C中不行。
3.在C++中,可以在结构体声明中声明甚至实现函数,在C中只能放函数指针。
4.在C++中,成员函数直接可以访问本结构的成员变量而无需传入,在C中,函数和结构并无直接关联。
***成员函数不影响结构体的大小
因为成员函数是放在公共区域的,只是在这个结构体域中而已。
类
类是一个特殊的结构体,只需要把上述结构体中的struct改成class,再加上public即可。
访问限定符:
public:正常访问 private:只能在类内部访问 protected:目前来说和private差别不大,但是主要差别体现在多态,继承的学习阶段 。
tip1:访问限定符只在编译阶段生效,编译好后,在运行阶段并没有限定。
tip2:结构体其实也能使用访问限定符,只是一般不去使用,因为它是用来兼容C结构体的。
tip3:结构体中默认是public,而类中默认是private。
注意点:
1.首先出现无穷递归/无穷调用的情况是在拷贝构造时候,拷贝构造的参数只有一个且必须使用引用传参,如果不使用引用传参则会出现无穷递归。
2.默认拷贝构造是浅拷贝,自己写的拷贝构造是深拷贝,若在牵扯到动态内存分配时候,自己不写拷贝构造函数,则会出现程序崩溃的情况,因为第二次析构的时候会引发错误。
3.在赋值运算符重载的时候bool operator=(const Date &d)假设是一个date类,那么我们可以理解为对=的赋值运算符重载,使用bool当然也可以,但是使用bool或者void 不可以多次赋值,例如d2=d1=d;但是若使用Date& operator=(const Date &d )则可以连续赋值。
构造函数
- 它是一个特殊的成员函数,它不存在返回值,名字和类名相同,在实例化对象的时候自动调用。
- 系统会自动提供一个默认的构造函数,如果自己实现了构造函数,则系统不再提供默认的构造函数。
- 构造函数可以存在参数,它与其它的构造函数是以函数重载的方式共同存在的。
- 拷贝构造函数指的是参数为本类其他对象的引用的构造函数,它在给对象初始化成本类其他对象时调用。系统会自动提供一个拷贝构造函数。
析构函数
- 析构函数是当一个栈被销毁前调用的,在C++中,当一个函数栈被销毁前,会调用栈中每一个函数的析构函数。
- 析构函数不存在参数也不存在返回值,它的名字是类名前加波浪线。
- 系统会自动提供一个什么都不做的析构函数。
浅拷贝:直接复制内存。
深拷贝:当成员中有指向堆的指针,就必须重新给该指针分配空间,然后将目标对象指针所指的空间的内容拷贝到新分配的空间。(如果不这样做,会导致两个指针指向同一片空间,从而在析构中多次释放。
初始化列表
1.狭义初始化
在定义变量的时候直接进行初始化的行为叫做狭义初始化。 例如 int a = 3;
2.广义初始化
第一次给变量赋值叫做初始化的情况叫做广义初始化。
int a;
……//跟a无关的代码
a=3;
初始化列表相当于狭义初始化,而构造函数内部相当于广义初始化。
所以初始化列表可以解决一些只能用狭义初始化进行初始化的变量,例如:const 变量 引用 没有无参构造的类的对象
explict
explict:阻止单参构造的不规范调用
单参数的构造函数:可以用=直接调用,例如:
假设CT类中有一个单参数的构造函数,参数类型为int或int相关类型,那么,"CT a = 3;"这种写法就是被允许的,但是这种写法十分别扭,看上去好像直接把3赋给了a,为了避免这种写法,可以在构造函数加explict,使得这样的写法变得无效。
赋值运算符重载
运算符重载:
将运算符看成函数,把它的几目当成参数,通过参数的类型识别出对应的操作方法,相当于函数重载。
运算符重载有指定的规则,规则根据运算符来制定。
类会自动提供一个赋值运算符的重载,执行的是浅拷贝,跟拷贝构造相同。
const成员函数
const加在成员函数的末尾,代表这个函数的this是const修饰的。
如果一个对象是const对象,那么它不能调用非const的成员函数。
取地址运算符重载
类会自动提供两个取地址运算符重载,一个是针对普通对象的,一个是针对const对象的。
静态成员
静态成员跟类走不跟对象走,类在他在,而一般成员是对象在他才在。所以静态成员可以通过类名直接调用,而普通成员必须通过对象调用。
1.静态成员变量
1.所有对象共享,无论谁改了,所有的一起改。
2.存储在全局区,不占用类的空间,所以取sizeof的时候不算在内。
3.不需要对象,可以直接用类名::调用
4.赋初值只能在类外,赋值时不加static,用"类型 类名::变量名 = n"直接赋值
2.静态成员函数
只能直接访问静态成员变量,无法访问其它的普通成员,因为它没有this指针。
友元
友元分为友元函数和友元类。
友元提供了一种突破封装的方式,但是会增加耦合度,破坏了封装,所以友元不宜多用。
C++是一种面向对象的语言,具有封装,多态,继承三大特性 ,在类中,只有类的成员函数才可以访问类的私有成员,非成员函数只能访问类的公有成员,为了使类的非成员函数访问类的成员,唯一的做法就是将成员定义为public,但这样做会破坏信息隐藏的特性。基于以上原因,引入友元函数解决。友元的作用是提高了程序的运行效率(即减少了类型检查和安全性检查等都需要时间开销),但它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。所以不建议使用。
友元函数
-
必须在类的说明中说明友元函数,说明时以关键字friend开头,后跟友元函数的函数原型,友元函数的说明可以出现在类的任何地方,包括private和public部分。
- 友元不是类的成员,不受类的声明区域public、private和protected的影响。
- 友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
-
友元函数不是类的成员函数,所以友元函数的实现与普通函数一样。在实现时不用“::”指示属于哪个类,只有成员函数才使用“::”作用域符号。
-
友元函数不能直接访问类的成员,只能访问对象成员。
-
我们试想一下,定义友元的目的是在不破坏隐藏(类的数据成员是private)的前提下,定义一个非成员函数访问私有数据成员。类的成员函数能“cout<< a<< endl;”的原因是有this指针。既然是私有数据成员,若要直接访问类的成员,且类的非静态成员必须与特定对象相对,所以必须通过类的对象访问类的私有数据成员。
友元函数可以访问对象的私有成员,但普通函数不行。 -
调用友元函数时,在实际参数中需要指出要访问的对象。
-
类与类之间的友元关系不能被继承。(友元不属于类的成员函数)
-
友元一般定义在类的外部,但需要在类体内进行声明,为了与该类的成员函数加以区分,在说明时前面加以关键字friend。友元函数不是成员函数,但它可以访问类的私有成员。友元的作用在于提高程序的运行效率,但它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。
-
对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,从而影响了程序的运行效率。
友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员
- 友元关系是单向的,不具有交换性
- 友元关系不能传递。 B是A的友元,C是B的友元,则不能说明C是A的友元
内部类
内部类是指一个类定义在另一个类的内部。这个内部类就叫做内部类。此时内部类是一个独立的类,不属于外部类,更不能通过外部类的对象去访问内部类。
内部类就是外部类的友元类,但是外部类不是内部类的友元。
特性:
- 内部类定义在外部类的public,protected,private都是可以的。
- 内部类可以直接访问外部类中的static,枚举成员,不需要外部类的对象/类名
- sizeof(外部类)=外部类,和内部类没有任何关系。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
class A
{
public:
class B
{
public:
void foo(const A& a)
{
cout<<k<<endl;
cout<<a.h<<endl;
}
};
private:
static int k;
int h;
};
int A::k=1;
int main()
{
A::B b;
b.foo(A());
return 0;
}
注意写的时候的一些规范,尤其是静态成员的初始化,以及内部类在主函数的调用等。