C++类和对象

一.引用.

1. 引用概念
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器
不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空

类型& 引用变量名(对象名) = 引用实体;
int a = 10;
int& ra = a;(必须和引用实体是同种类型)
2. 引用特性
a. 引用在定义时必须初始化
b. 一个变量可以有多个引用
c. 引用一旦引用一个实体,再不能引用其他实体
3. ## 常引用 ##
const int a = 10;
//int& ra = a; // 该语句编译时会出错,a为常量
const int& ra = a;

表达式 int &i = j; 将会被编译器转化成 int *const i = &j;
const对指针和引用
①指针
常量指针定义”const int* pointer=&a”告诉编译器,*pointer是常量,不能将*pointer作为左值进行操作。
常量引用:指向常量的引用,在引用定义语句的类型前加const,表示指向的对象是常量。也跟指针一样不能利用引用对指向的变量进行重新赋值操作。
指针常量定义”int* const pointer=&b”告诉编译器,pointer是常量,不能作为左值进行操作,但是允许修改间接访问值,即*pointer可以修改
常量指针常量:指向常量的指针常量,可以定义一个指向常量的指针常量,它必须在定义时初始化。常量指针常量定义”const int* const pointer=&c”告诉编译器,pointer和*pointer都是常量,他们都不能作为左值进行操作。
②引用
表达式 int &i = j; 将会被编译器转化成 int *const i = &j;
程序决不能给引用本身重新赋值,使他指向另一个变量,因此引用总是const;所以就不存在所谓的”常量引用常量”,
只有const int & a=1;没有:Const int const& a=1
区分const是修饰指针,还是修饰指针指向的数据——画一条垂直穿过指针声明的星号(*),如果const出现在线的左边,指针指向的数据为常量;如果const出现在右边,指针本身为常量。

4. 使用场景
a. 作为函数形参
b. 作为函数返回值注意:不能返回栈空间上的引用
5. 引用和指针的区别
相同点:底层的实现方式相同,都是按照指针的方式来实现的
不同
引用在定义时必须初始化,指针没有要求
一旦一个引用被初始化为指向一个对象,就不能再指向其他对象,而
指针可以在任何时候指向任何一个同类型对象
没有NULL引用,但有NULL指针
在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地
址*空间所占字节个数
引用自加改变变量的内容,指针自加改变了指针指向
有多级指针,但是没有多级引用
指针需要手动寻址,引用通过编译器实现寻址
引用比指针使用起来相对更安全


this指针


构造与析构

一.构造函数
1.构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时,由编
译器自动调用,在对象的生命周期内只调用一次,保证每个数据成员都有
一个合适的初始值
2①.无参构造函数和带有缺省值的构造函数都认为是缺省的构造函数,并
且缺省的构造函数只能有一个

1)如果一个参数设定了缺省值时,其右边的参数都要有缺省值

如:int mal(int a, int b=3, int c=6, int d=8)    正确,按从右到左顺序设定默认值。
        int mal(int a=6, int b=3, int c=5, int d)   错误,未按照从右到左设定默认值。c设定缺省值了,而其右边的d没有缺省值。
    (2)调用时,则遵循参数调用顺序,自左到右逐个调用。    
如:void   mal(int a, int b=3, int c=5);      //默认参数
          mal(3, 8, 9 );                    //调用时有指定参数,则不使用默认参数
          mal(3, 5);                       //调用时只指定两个参数,按从左到右顺序调用,相当于mal(3,5,5);
           mal(3);                           //调用时只指定1个参数,按从左到右顺序调用,相当于mal(5,3,5);
           mal( );                           //错误,因为a没有默认值
           mal(3, , 9)                      //错误,应按从左到右顺序逐个调用
再如: void mal(int a=8, int b=3, int c=5);      //默认参数
          mal( );    //正确,调用所有默认参数,相当于mal(8,3,5);
重载函数与默认参数重叠导致的二义性问题:
func(int);                                                                               //重载函数1,只有1个参数,无默认参数
func(int, int =4);                                                                   //重载函数2,有2个参数,有1个默认参数
func(int a=3, int b=4, int c=6);                                            //重载函数3,有3个参数,有3个默认参数
fucn(float a=3.0, float b=4.0 float c=5.0);                             //重载函数4,有3个参数,有3个默认参数
fucn(float a=3.0, float b=4.0 float c=5.0 float d=7.9 );          //重载函数5,有4个参数,有4个默认参数

func(2);                  //可调用前3个函数,出现二义性
func(2.0);               //可调用后2个函数,出现二义性

所以当重载函数与默认参数共同使用时,要注意出现二义性问题。

**3.初始化列表
类中包含以下成员,一定要放在初始化列表位置进行初始化:
引用成员变量
const成员变量
类类型成员(该类有非缺省的构造函数)
**

class CExample {
public:
int a;
float b;
//构造函数初始化列表
CExample(): a(0),b(8.8)
{}
//构造函数内部赋值
CExample()
{
a=0;
b=8.8;
}
};

3.初始化和赋值对内置类型(就是基本数据类型)的成员没有什么大的区别,像上面的任一个构造函数都可以.

上面的构造函数(使用初始化列表的构造函数)显式的初始化类的成员;
而没使用初始化列表的构造函数是对类的成员赋值,并没有进行显式的初始化.当一个类中有非内置类型成员变量时,若没有显示初始化这个非内置类型成员变量,那么编译器会隐士调用该非内置类型成员变量的默认构造函数,而如果用了成员初始化构造函数,对这个变量有了显示初始化,系统就不会再隐士调用它的默认构造函数了,所以这里少了一次构造。

class Food
{
public:
    Food(int type = 10)
    {
        m_type = 10;
    }
    Food(Food &other)                 //拷贝构造函数
    {
        m_type = other.m_type;
    }
    Food & operator =(Food &other)      //重载赋值=函数
    {
        m_type = other.m_type;
        return *this;
    }
private:
    int m_type;
};

(1)构造函数赋值方式 初始化成员对象m_food
class Dog: public Animal
{
public:
    Dog(Food &food)
      //:m_food(food)  
    {
        m_food = food;               //初始化 成员对象
    }
private:
    Food m_food;
};
//使用
Food fd;
Dog dog(fd);   //
Dog dog(fd);结果:
先执行了   对象类型构造函数Food(int type = 10)——> 
然后在执行 对象类型构造函数Food & operator =(Food &other)
?

(2)构造函数初始化列表方式
class Dog: public Animal
{
public:
    Dog(Food &food)
      :m_food(food)                  //初始化 成员对象
    {
        //m_food = food;               
    }
private:
    Food m_food;
};
//使用
Food fd;
Dog dog(fd);   //
Dog dog(fd);结果:执行Food(Food &other)拷贝构造函数完成初始化

二.析构函数
1.析构函数:与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类的一些资源清理和汕尾工作.
析构函数在类名(即构造函数名)加上字符~
析构函数无参数无返回值
一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省
的析构函数
对象生命周期结束时,C++编译系统系统自动调用析构函数
注意析构函数体内并不是删除对象,而是做一些清理工作

运算符重载和友元

一.重载操作符
1. 概念
重载操作符是具有特殊函数名的函数,关键字operator后面接需要定义的
操作符符号。操作符重载也是一个函数,具有返回值和形参表。它的形参
数目与操作符的操作数目相同,使用运算符重载可以提高代码的可读性
返回值 operator 操作符(参数列表)。
就像一个词在不同的场景下有不同的含义

2. 注意
不能通过连接其他符号来创建新的操作符:比如operator@
重载操作符必须有一个类类型或者枚举类型的操作数
用于内置类型的操作符,其含义不能改变,例如:内置的整型+,
能改变其含义
作为类成员的重载函数,其形参看起来比操作数数目少1成员函数的
操作符有一个默认的形参this,限定为第一个形参.
一般将算术操作符定义为非成员函数,将赋值运算符定义成员函数
操作符定义为非类的成员函数时,一般将其定义为类的友元
== 和 != 操作符一般要成对重载
下标操作符[]:一个非const成员并返回引用,一个是const成员并
返回引用
解引用操作符*和->操作符,不显示任何参数
前置式++/–必须返回被增量或者减量的引用,后缀式操作符必须返
回旧值,并且应该是值返回而不是引用返回
输入操作符>>和输出操作符<<必须定义为类的友元函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值