一.const和常函数
1.常量指针和指针常量
const int* p1;
常量指针:修饰的是指针所指向的空间,也就是不能更改(*p1)的值,但是可以改变指针的
指向,也就是可以修改p1,也可以读取。不可以通过当前指针修改指向的空间但是可以通过其他方法 。
int* const p2=&a;//指针常量要初始化
指针常量:const修饰的是p2指针,可以通过指针修改空间的值*p2 = 30。不能修改指针的值
p2 = &b;是非法的
const int* const a=&b;//此种就是什么都不能修改,安全级别最高
2. 初始化参数列表:
class t
{
public:
const int m_a;
int m_b;
//普通成员也可也以在参数列表初始化,用逗号分隔,初始化成员的顺序是类成员在类中声明的先后顺序
//在构造函数初始化参数列表进行初始化,在构造函数内对于const修饰的m_a就是赋值了
t() :m_a(10)//构造函数的初始化参数列表
{
m_b = 10;
m_c = 20;
}
t(int a):m_a(10),m_b(a)
{}
};
3.常函数:防止修改成员变量的值
注意: 普通成员函数才有常函数。C++中构造函数,静态成员函数,析构函数,全局成员函数都不能是常成员函数。构造成员函数的用途是对对象初始化,成员函数主要是用来被对象调用的,如果构造函数被设置成const,就不能更改成员变量,失去了其作为构造函数的意义。同理析构函数。全局成员函数和静态成员函数static其函数体内部没有this指针,所以也不能是常成员函数
void show()const
{
m_c = 10;//经过mutable修饰了,可以修改
}
参数列表后加上const修饰即为常函数,const实际上修饰的是this指针.const t* const this。
mutable int m_c;
如果在常函数中修改成员变量,要在变量前加mutable修饰
class t
{
public:
const int m_a;
void show()const
{
}
void play() //t* const this=&t;
{
}
};
int main()
{
const t tmp;//常量对象
tmp.play();//非法的。因为常量对象的为const t* this而play 为t* const this 指针安全级别降低不允许
tmp.show();//合法。因为show为常量指针,const t* const this,指针安全级别上升。
return 0;
}
扩大权限是不可以的,缩小权限是可以的
非const对象可以调用常函数也可以调用非常函数,而const对象只能调用常函数
4.编译期和运行期
编译期:编译期是指把源程序交给编译器编译,生成的过程,最终得到可执行文件。
运行期:将可执行文件交给操作系统执行,直到程序退出,执行的目的是为了实现程序的功能
类是编译期的概念,包括成员的访问修饰符(public和private等)和作用域。全局或者静态全局的编译期就存在了
对象是运行期的概念,包括定义类的实例,引用,指针等使用其类的成员。new 动态申请空间为运行期
5.内联函数:通过关键字inline对函数进行修饰
inline;关键字:内联,修饰函数。当调用时就是内敛展开,将代码进行替换。在编译阶段进行内敛展开。
优缺点:程序执行的效率提高了,代价是内存变多了。调用一次相当于展开一次,空间换时间。如果函数调用的开销时间远小于函数体代码的执行时间,那么效率提高并不多。所以一般函数体较长,且出现for while switch等不适合当作内联函数。
建议性关键字:编译器会根据具体情况来决定是否把其认定为内联函数,递归函数即使加上inline,编译器也不会认为是内联函数。
在类内部声明并定义的函数默认为内联函数。
二.类之间的关系:组合,依赖,关联,聚合
1.组合:一个类里包含另一个类的对象。部分与整体,包含与被包含。有生命周期约束关系
class ss
{
private:
int a;
};
class sss
{
ss a;
};
2.依赖:完成某个功能必需用到被依赖的对象,一般当工具关系。没有生命周期关系
3.关联:可有可无的平等关系
4.聚合:被聚合的对象组合起来(容器),统一管理,有生命周期
三.类之间的纵向关系:继承(描述的是类之间的纵向关系)
1.父类(基类),子类(派生类)。子类可以使用父类的成员,方法如下
//父类(基类)
class Cfa
{
public:
int m_fa;
Cfa()
{
m_fa = 10;
}
};
class Cson :public Cfa //继承关系
{
public:
int m_son;
Cson()
{
m_son = 20;
}
void show()
{
m_fa = 21; //调用父类成员,给其赋值
cout << m_fa << endl; //输出为21
}
};
2.当子类和父类有同名成员时侯,可以通过作用域来区分。具体如下:
class Cfa
{
public:
int m_fa;
int m_mon;
Cfa()
{
m_mon = 20;
}
};
//子类和父类有同名但不同值的变量m_mon,通过限制作用域来决定使用谁
class Cson :public Cfa
{
public:
int m_mon;
Cson()
{
m_mon = 100;
}
};
int main()
{
Cson son;
cout << son.m_mon << endl;//输出为100,因为是Cson的类,所以默认调用子类的m_mon
cout << son.Cfa::m_mon << endl;//输出为20,通过作用域进行限制
//所以son.m_mon也相当于son.Cson.m_mon
return 0;
}
注意对于成员函数来说,当父类和子类中如果有同名不同参的函数,则它们不属于函数重载
3. 定义子类对象包含父类成员(也就是对象大小是父类成员所占字节数加上子类成员所占字节数)。内存分布,先父类成员,后子类成员。
4.定义子类对象,构造顺序。父类->子类
class Cson :public Cfa
{
public:
int m_mon;
Cson():Cfa() //当你声明一个子类对象时候,先调用子类构造。
{ //在子类构造的初始化参数列表里调用父类构造,然后在进入子类的构造函数
m_mon = 100;
}
};
以上为无参构造若要父类是有参的构造,则需要在初始化参数列表中手动调用参数如下:
class Cson :public Cfa
{
public:
int m_mon;
Cson():Cfa(10,1000) //在这里进行传参
{
m_mon = 100;
}
};
5.子类对象在生命周期结束时,调用析构的顺序。子类->父类(前提定义的是子类对象)。
同时注意:对于析构函数主要用于通过delete回收额外申请的内存空间(即new出来的空间)
6.继承好处:将一些功能相似的类的公共成员,单独抽离出来,形成一个类,这个类就是父类,子类继承父类,包含了这些 公共的成员。提高了程序代码的复用性,扩展性.
7.继承方式:公有继承(public) 保护继承(protected) 私有继承(private)
继承方式描述了父类成员在子类中所表现的属性,即使用范围
public:公有继承
父类 子类
public public
protected protected
private 不可访问
protected:保护继承
父类 子类
public protected
protected protected
private 不可访问
private:私有继承
父类 子类
public private
protected private
private 不可访问
8.隐藏: