C++常函数和继承的基础

一.const和常函数

1.常量指针和指针常量

const int* p1;

常量指针:修饰的是指针所指向的空间,也就是不能更改(*p1)的值,但是可以改变指针的
指向,也就是可以修改p1,也可以读取。不可以通过当前指针修改指向的空间但是可以通过其他方法 。

int* const p2=&a;//指针常量要初始化

 指针常量:const修饰的是p2指针,可以通过指针修改空间的值*p2 = 30。不能修改指针的值
 p2 = &b;是非法的

const int* const a=&b;//此种就是什么都不能修改,安全级别最高

2. 初始化参数列表:

当类中有 const 类型的变量时,在定义的时候必须要初始化,而这个初始化操作是在 初始化参数列
中完成的,而构造函数的函数体代码中进行的操作严格来说是赋值,先执行初始化列表,在执行
构造函数体中的代码。对于普通的变量来说也可在初始化参数列表中初始化。写法:在构造函数的
参数列表后加上一个冒号 : 后面是初始化的成员,用圆括号 () 的形式指定初始化值,多个成员用
逗号 , 分割。
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.子类对象在生命周期结束时,调用析构的顺序。子类->父类(前提定义的是子类对象)。

子类对象的生命周期结束后,因为是子类所以自动调用子类析构,当析构执行完
了,才会回收对象分配的空间,当然这个空间包含创建的父类的成员,那么回收父类成员前,自动
调用父类的析构。如果是 new 出来的子类对象,同理。

同时注意:对于析构函数主要用于通过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.隐藏:

那对于父类和子类中,如果有同名的函数但是参数列表不同,则不能够自动区分,因为他们之间的
关系并不是函数重载的关系,作用域不同,必须使用 类名 :: 去区分到底该调用哪个函数。子类中
的重名函数我们称之为 隐藏

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值