类和对象相关知识点

1. 结构体

在引入类时,先回顾结构体定义以及对齐等相关知识

(1)结构体的定义

  struct student

{

int age;

char sex;

char name;

};

(2)结构体的对齐

宗旨为:先内部对齐,再整体对齐

  struct student

{

int age;//对齐参数为4,对齐为4个字节

char sex;//对齐参数为1,因为4是1的倍数,所以为5

double b;//对齐参数为4,5不是4的倍数,所以在之前添3位

char name;//对齐参数为1,所以为9位

};//又因为9不是最大对齐参数4的倍数,所以添3位所以结构体大小为12

(3)在结构体内定义函数(在c++中适用)

struct student

{

int _age;

char _sex[5];

char _name[10];

void set(const char*name, const char*sex, int age)//在c++中,可以在结构体内定义函数

{

strcpy(_name, name);

strcpy(_sex, sex);

_age = age;

 

}

};

2. 

(1)类的声明以及定义

--类的定义通常有两种方式

 ·声明和定义都在.h文件中

class student

{

int _age;

char _sex[5];

char _name[10];

void set(const char*name, const char*sex, int age)

{

strcpy(_name, name);

strcpy(_sex, sex);

_age = age;

 

}

};

·在.h文件中声明,在.cpp文件中定义

(.h文件)

class student

{

int _age;

char _sex[5];

char _name[10];

void set(const char*name, const char*sex, int age)

}

 

(.cpp文件)

void student::set(const char*name, const char*sex, int age)

{

strcpy(_name, name);

strcpy(_sex, sex);

_age = age;

}

(2)类的作用域

  ·c++有三大特性:封装、继承、多态

  ·访问限定符分为三类:公有(public),私有(private),保护(protected)

  ·在类中成员默认为私有的,在结构体中默认为公有的

保护和私有在类外无法直接使用

namespace N1

{

int a = 10;

}

 

int a = 10;

void func()

{

cout<<"func()" << endl;

}

 

 

class student

{

public:

int _age;

char _sex[5];

char _name[10];

void set(int a)

{

a = a;

}

void print()

{

cout << a << endl;

}

private:

int a;//私有,类外无法使用

};

int main()

{

student s;

s.set(30);//进入类中,

cout << a << endl;//因为在类中只是进行了赋值,所以输出流找最近的a的值为10

cout << N1::a << endl;//在明明空间中找到了a.所以值为20

s.print();

return 0;

}

(3)类的大小

· 类没有实际的大小,只包含了类中成员变量

·而空累的默认大小为1

由此,我们可以提出一个问题,编译器是如何识别类的?

·首先,通过类名找到类

·然后进入到类中识别成员变量

·最后对成员函数进行修改

·在类的成员函数中,有一种默认的指针,称为this指针

 --this指针

  ·this指针不属于对象中的一员,它相当于类的另一种表现形式

  ·this指针是成员函数的第一个默认隐含参数,编译器自动调用与维护,类编写者不能显示传递

  ·this指针的形式为const* :指针不可以改,但是它所指向的空间的内容可修改

·this指针只有在非静态函数中才可以使用

 

·this作为指针,与引用最大的区别为

  --引用必须要初始化,引用可以当成 int*const 类型的指针来处理

  --指针在自加时是给所指的类型自加,偏移了大小,但是引用是给自己自加,所以引用相对而言安全

--没有null引用,但是有null指针

--引用的大小由引用类型决定,但是指针的大小只是由自身的空间所占字节数决定

--没有多级引用,但是有多级指针

--指针需要手动寻址,引用通过编译器寻址

既然提到了引用,下面就来分析引用的用法

 

3.引用

1)引用的概念

 引用不是新定义一个变量,而是给已经存在的变量起一个新的名字,所以编译器不会给引用开辟新的空间,因为它与引用的变量占同一块空间。

void func()

{

int a = 10;

int& ra = a;//类型& +引用变量=引用实体

int& rb = a;

 

}

--引用在定义时必须初始化  

--一个变量可以有多个引用

-- 但是一个引用只可以引用一个实体

2)常引用

--const int a = 10;

const int& ra = a;//引用了a常量,并且&ra的内容无法更改

--double b = 20;

const int &rb = b;//编译时会出错,因为类型不同

3)数组引用

    int ar[10];

int(&arr)[10] = ar;//数组引用形式

(4)函数参数引用

    --void swap(int& left, int &right)//通过参数引用的方法,相当于指针

--函数返回值

int& test(int& left)//通过参数引用的方法,相当于指针

{

left += 1;

return left;

}

--int& test(int left){

return left;//在栈上开辟了left形参的空间,所以不能返回返回值若为引用类型,则需要在函数内建立形参

引用的分析就先到这里,下面进入回到类的知识点中。接下来,主要介绍类的六个默认函数

4.类的默认成员函数

  1)构造函数

  --构造函数是一个特殊的成员函数,因为它的名字与类名相同,并且它在对象的生命周期内只调用一次,保证每个数据成员都有一个合适的初始值。

  --class student

{

public:

student(int age, char sex)

{

_age = age;

_sex = sex;

}

private:

int _age;

char _sex;

}

int main()

{

student s1(16, '女');

}

--构造函数可以重载,实参决定它调用那个构造函数

--有初始化列表(可以不用)

  student(int age, char sex)

: _age(age)

, _sex(sex)

{}

   初始化列表是以冒号开始,逗号分隔的表达式

   有以下几种成员,必须要放在初始化列表的位置进行初始化

  ·引用成员变量(在拷贝析构函数中会有体现)

  ·const成员变量

  · 类类型成员 (有非缺省的构造函数)

2)拷贝构造函数

    只有单个形参,该形参为本身类型对象的引用,创建对象时使用已经存在的对象进行引用

    public:

student(int age, char sex)

: _age(age)

, _sex(sex)

{}

student(const student &s)//引用

:_age(s._age)

, _sex(s._sex)

{}

}

 

int main()

{

    student s1(16, '女');

student s2(s1);//student (const student &s1)

}

(3)析构函数

   --概念:和构造函数的功能相反,在对象被销毁的时候,由编译器自动调用,完成类的一些资源清理和汕尾工作。

  student(const student &s)

:_age(s._age)

, _sex(s._sex)

{}

~student()   //析构函数的格式

{

if (p)

{

free(p);  

}

}

--特性:

·没有参数返回值

·一个类中只有一个析构函数,系统自动生成缺省的析构函数

·当一个对象生命周期结束后,(即完成了自己任务之后)系统会自动调用析构函数

·析构函数并不是删除对象,而是做一些清理工作。

(4)赋值操作符重载

  --重载操作符是具有特殊函数名的函数,所以它也是一种函数

格式为oprator接所要定义的操作符符号。

具有返回值和形参表,形参数和操作符的操作数目相同

  --student &operator=(const student &s)

{

if (this != &s)

{

_age = s._age;

_sex = s._sex;

        }

return *this;

}

     int main()

{

    student s1(16, '女');

student s2(s1);//student (const student &s1)

student s3 = s2;//因为自定义对象类不能直接赋值,所以需要对= 进行操作符重载

student s4 = s3 = s2;

//相当于s4.oprator=(s3.oprator=(s2));

}

--注意:

  ·不能通过连接其他符号来创建新的操作符:比如operator@

  ·重载操作符必须有一个类型或者枚举类型的操作数

  ·用于内置类型的操作符,其含义不能改变,例如:内置的整型+ ,不能改变其含义

  ·重载函数的形参有一个默认的形参this,限定为第一个形参

  · 一般的算术操作符重载为非成员函数,但是赋值运算符为成员函数

  ·==和!=一般要成对重载

  ·下标操作符[]:一个形参为非const成员返回引用,一个是const成员返回引用

  · 解运算符重载*和->操作符,不显示任何参数

  · 前置时++ 必须返回被增量的引用,后缀式必须返回旧值,并且是引用返回。

   s2 = ++s1;

//相当于s1.operator++(),无参数

//student &operator++(),返回值为引用

 

s2 = s1++;//s1.opreator++(10)必须传参

//student operator++(int),参数默认为int不需要改变外部的值

  ·输入操作符>>和输出操作符<< 必须定义为类的友元函数。

(5)取地址操作符以及const修饰的取地址运算符重载

     --

    Class student

{

const student* operator&()const-》修饰this指针

{

return this;

}

student* operator&()

{

return this;

}  

}

int main()

{

    student s3;

const student s4;

cout << &s3 << endl;

(6)除了以上的6个默认函数之外,还有一个函数比较重要 ,那就是友元函数

   --概念:

     可以直接访问类的私有成员,它是定义在外部的普通函数,不属于任何类,但是需要在类的内部声明,声明时要加friend关键字

   --特点:

     ·该函数能访问类声明的私有部分

     ·该函数位于类的作用域中

     ·该函数必须有一个this指针

   --class student

{

friend void print(const student &s);//友元函数的声明

 

public:

student(int age, char sex)

:_age(age)

, _sex(sex)

{}

private:

int _age;

char _sex;

};

void print(const student &s)//在类外定义

{

cout << s._sex <<" "<< s._age << endl;

}

int main()

{

student s3(16, '女');

print(s3);//通过友元函数可以访问到类内部的成员

return 0;

}

--说明

·友元函数可以访问类的私有成员,但不是类的成员函数

·不能用const修饰

·可以在类的任何地方声明

·一个函数可以是对个类的友元函数(相当于进入了另一个类内,被授权)

--优缺点

·优点: 提高了程序的运行效率,不必开辟栈帧,空间

·缺点:破坏了类的封装性和隐藏性

--注意

·友元关系不能继承

·友元关系是单向的,不具有交换性

·不能传递

 

   

   

 

 

猜你喜欢

转载自blog.csdn.net/ning_zhi_t/article/details/80244005