c++类和对象概述
2022.4.16 更新中,如果内容太多会考虑分批
目录大概如下

-------------------------
第一节 类数据成员
类中的数据通常分为:
类数据成员,包括普通变量,staitc变量,const变量,const static变量,
类成员函数,包括普通成员函数,static成员函数,const成员函数,默认&指定构造函数,析构函数,运算符重载,
友元函数,友元函数不属于类,但可以访问类的任意成员。
普通类成员
类成员分为private,public,protected成员
private成员只能被基类的成员函数和友元函数所访问,在类外无法直接访问,对内可见,对外不可见,
protected成员在派生类(即子类)中是可以被派生类的成员函数和友元函数访问的, 在基类中同private,简单来说就像为了让基类的private成员能被子类继续访问,衍生出的protect属性,
public成员在类外类内都可以直接访问调用
思考:为什么要有protected的存在?
如果不考虑派生类的存在,private与public足够实现单个类的封装,但是派生类的设计者,其目的一般是为了扩展基类的功能,通常情况下是需要访问一些基类的非公共接口和数据成员来实现相关功能的扩展,如果基类完全封闭其成员,对于派生类的功能扩展实现会有阻碍,这里后面再补充实例
protected优点在于
能让派生类更清晰直接的扩展基类功能,并复用基类的数据结构
但缺点在于
如果后期基类的数据成员结构有所变动,可能会影响到派生类的行为,因为派生类也会使用这些成员,而且你无法保证派生类的设计者对基类成员的使用规则是完全合理的,
如果基类不存在protected成员,显然派生类永远只能通过共有接口调用基类成员,即便基类成员内部有所变化,只要对外公共接口的功能不变(这也是必需的,否则所有调用该接口的代码都需要重写,显然不合理)就不会影响到派生类,
除非你想完全封闭基类,否则应当保留部分可被派生类使用的protect成员
但需要注意,派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊的访问权限
形如下面的
class base{protected:int basedata};
class driver:public base{};
drive::getdata(base &b)
{
b.basedata=1;//无法通过编译,这里直接通过基类对象(包括引用和指针)来访问基类的protect成员
this->basedata=1;//显然是没问题的,派生类继承了基类的protect成员并拥有访问权限,
}

driver::getdata(driver&d)
{
d.basedata=1;//也是ok的,通过派生类对象来访问基类的protect成员
}
思考1:为什么不允许访问,能够访问的话带来什么后果?
首先已经明确,对于基类本身来说,protected成员等同于private成员,是不允许在其成员作用域外使用的(也就是类本身才是成员作用域,类外不算)
那么对于形如 b.basedata=1;来说,这是在派生类里调用,已经超过基类成员的作用域,从作用域来说就是不可访问的,如果能够访问,则会破坏基类数据成员的安全性,造成未知后果
思考2:如果实际传入的本身就是派生类引用呢?因为基类引用可以指向派生类,所以形参(base &b)是兼容传入派生类引用的
呵呵,只要形参是基类引用或者指针,函数体中就不允许调用基类成员,编译都通不过,你传勾八呢
普通数据成员初始化
见构造函数相关内容
static成员
类数据成员可以声明为static类型,有如下几个特性
1.static数据成员属于类本身,对于一个类,无论定义多少个对象,一个static成员只存在一份拷贝,该份拷贝存在静态内存区,当你对各个对象的static成员取地址肯定会发现其地址都是一样的
2.static修饰的变量先于对象实体存在,所以static修饰的变量要在类外初始化。
如果声明了static成员变量但不初始化,编译会报错,
思考,为何一定要初始化,普通的全局变量也没这个要求啊?
这里更准确的说是一定要定义,我们要分清楚变量声明与定义的区别,变量声明不分配内存,定义分配实际内存,除了类中变量以外,大部分时候变量的声明与定义都是同时进行的(除了extern 声明外部变量),当你声明int i;的时候,也同时定义了i,编译器便会为i分配实际堆栈空间,这个时候我们才能访问使用i变量。
回过头来看static成员,类中的只是声明而非定义,此时stadata并没有分配空间,任何手段都无法访问到stadata变量因为它根本就不存在,你想要使用stadata必须明确给出定义,就如图上的,定义后内存中就有stadata的位置了,你才能使用它,初始化只是一个赋值的动作而已,要不要无所谓,毕竟静态变量都会默认初始化的,
思考,能否通过构造函数初始化?
同上,你在构造函数中初始化的写法是啥,stadata=xx,然而这个时候stadata连实体都没有,你初始化个勾八呢,而如果你已经定义过stadata,那么构造函数里的就只是单纯的赋值操作而已,而且没啥意义,每个构造函数都赋值一次,修改的只是同一个变量而已,多此一举
思考,为何不能在类内初始化?
你是指形如static int stadata=1; 这种吗,这个点扯起来就可多了,
那让我们先说说类内初始化值,从c++11开始,支持在声明类成员的时候给定一个初始值,int a=10,那样在创建对象的时候,如果这个成员没有被明确的初始化,那么就用类内初始值来初始化,
如果你已经在外部定义了stadata,再设置类内初始值,一点问题没有,而且会被正确初始化,这个没毛病可挑,毕竟stadata已经实际分配内存了,类内初始值只是多个赋值动作而已,
但如果不定义,仅仅设置类内初始值,编译器会报错,
可以看出编译器本身对类内静态成员的初始化有个要求:这个静态成员必须要是常量类型,
那很简单,static const int stadata=1,就行了,或者使用constexpr,constexpr static int stadata=1(有关于const和constexpr请见专题 const关键字)
当你使用const static组合的时候,对应的变量也会按照c++ 中const的原则,为这个变量生成符号表,用对变量使用到的地方都替换成对应的值,这种情况下对于变量不会分配实际内存,只是做编译时替换,如果你的代码没有使用到这个变量的引用或者指针类型时(因为引用和指针会取地址,需要实际存在的变量)这种情况下你是不需要在类外进行定义的。
但如果还会用到这个变量,还得进行类外定义来让编译器为其分配内存,
写到这里的时候感觉逻辑不太对,我自己尝试类外定义的话会报错,重复定义,先打住,正在stackflow上查信息,感觉需要深入了解const和static的组合应用
初始化
1.static数据成员的初始化要在cpp文件中进行,不能在类定义的h文件中进行,因为如果在头文件中进行初始化,则其他包含了该头文件的地方都会对该变量重复定义,除非你这个头文件不被任何其他人包含,但这显然不可能,头文件就是被其他文件调用的,所以我们要在类定义的头文件对应的源文件中初始化static变量
初始化的时候不需考虑访问权限,直接使用base::staticdata显示初始化即可,
int test_base::stapridata = 1;
int test_base::staprodata = 2;
int test_base::stapubdata = 3;
访问方式
1.public成员,因为static成员是属于类的,用类作用域限定符即可访问,如base::staticdata;
2.public成员,通过特定对象也可以访问到,如base b1;b1.staticdata;
3.protected或者private成员只能通过公共接口来访问了,
另外
1.用static修饰的成员变量在对象中是不占内存的,因为他不是跟对象一起在堆或者栈中生成,用static修饰的变量在静态存储区生成的,所以对象的字节大小不包含static数据,
2.由于static修饰的类成员属于类,不属于对象,因此static类成员函数是没有this指针的。而因为没有this指针,所以static类成员函数不能访问非static的类成员,只能访问 static修饰的类成员。

浙公网安备 33010602011771号