C++ 类和对象的使用

1.对象的初始化——构造函数

在对类对象的数据进行赋值时,可以在类中直接赋值,但不推荐,因为类不是实体,是抽象的概念,不占用存储空间。

class Student
{
public:
    string addr = "北京市朝阳区";
    void display();

private:
    int num = 0;
    string name = "Tom";
    char sex = 'M';
};

也可以用成员函数赋值。


void Student::SetStudent() {
    num = 1;
    name = "Jack";
    sex = 'M';
    addr = "北京市昌平区";
}

当类中的数据成员较多时,上面两种方法显然效率不高。C++在类中提供了构造函数来对类对象进行初始化。

(1)无参构造函数
在C++中, 构造函数与类同名,是一种特殊的成员函数,与其他成员函数不同,它是在 建立对象时自动调用的,不需要用户来调用。

// 在类内
    Student(){
        num = 2;
        name = "Merry";
        sex = 'F';
        addr = "北京市西城区";
    }

// 在类外
    Student::Student(){
        num = 2;
        name = "Merry";
        sex = 'F';
        addr = "北京市西城区";
    }

注意
<1>构造函数只是对类对象进行初始化,不需要声明类型;
<2>构造函数在创建对象时,由系统自动调用。每创建一次就调用一次。
<3>如果用户没有定义,系统会自动生成一个没有参数,没有函数体,不执行初始化操作的构造函数。

(2)带参构造函数
<1>默认参数构造函数
在建立对象时,不用给出实参的构造函数,称为 默认构造函数

类中声明:
Student(int n = 4, string name_ = "Jobs", char s = 'M', string address = "美国");
类外定义:
Student::Student(int n, string name_, char s, string address)
{
    num = n;
    name = name_;
    sex = s;
    addr = address;
}
主函数调用:
Student stud;

注意
无参构造函数也属于默认构造函数; 一个类只能有一个默认构造函数

<2>非默认参数构造函数
对不同对象初始化不同的初值。

类中声明:
    Student(int, string, char, string);
类外实现:
Student::Student(int number, string name_, char sex_, string address){
    num = number;
    name = name_;
    sex = sex_;
    addr = address;
}
主函数中调用:
    Student stud1(3, "God", 'M', "上海市");

注意:在类中声明带参构造函数时,形参可以只有类型,无形参名。
在这里插入图片描述

(3)参数初始化表
参数初始化表减少函数体长度,使得函数结构精简。

    Student(int n, string name_, char s, string address, int score[]):num(n), name(name_), sex(s), addr(address)
    {
        for(int i = 0;i < 12;i++)
        {
            Score[i] = score[i];
        }
    }

注意:若果数据成员是数组,则应该在构造函数的函数体中对其进行初始化,而不能在参数初始化表中对其进行初始化。

(4)构造函数的重载
在类中可以声明多个构造函数,包括无参构造函数和带参构造函数。

类中声明:
    Student();
    Student(int, string, char, string);
    
类外定义:
Student::Student() {}

Student::Student(int n, string name_, char s, string address)
{
    num = n;
    name = name_;
    sex = s;
    addr = address;
}

主函数中调用:
    Student stud;
    stud.display();
    
    Student stud1(3, "God", 'M', "上海市");
    stud1.display();

或 使用参数初始化表。

类内声明:
    Student();
    Student(int n, string name_, char s, string address):num(n), name(name_), sex(s), addr(address){ }

类外定义:
Student::Student() {}

主函数中调用:
    Student stud;
    stud.display();

    Student stud1(3, "God", 'M', "上海市");
    stud1.display();

注意:参数初始化表不需要类外定义。

(5)析构函数
析构函数(destructor)也是一种特殊的成员函数,它的作用与构造函数相反,在类名前加一个“~”符号。作用是 在撤销对象占用的内存之前完成一些清理工作,使得这部分内存可以被重新利用。
当对象生命周期结束时,自动执行析构函数。

    ~Student()
    {
        cout<<"deconstructed!!!"<<endl;
    }

注意
<1>函数中的局部对象释放前,自动执行析构函数;
<2>静态(static)局部对象在函数调用结束时对象并不释放,在main函数结束或调用exit时执行析构函数;
<3>全局对象在main函数结束或调用exit时执行析构函数;
<4>用new运算符动态建立的对象,使用delete运算符释放时,调用析构函数;
<5>一个类可以有多个构造函数,但只能有一个析构函数。

构造函数和析构函数的调用顺序遵循:先构造的后析构,后构造的先析构

2.对象数组

对象数组的每一个元素都是同类的对象

    Student student[3] = {
            Student(),
            Student(3, "God", 'M', "加拿大"),
            Student(4, "Med", 'M', "美国"),
    };

3.对象指针

(1)指向对象的指针
一个对象的存储空间的起始地址就是对象的指针,定义一个指针变量指向起始地址,就是指向对象的指针变量。

    Student *p, s1;
    p = &s1;
    p->display();
    (*p).display();

(2)指向对象成员的指针
对象成员也有地址, 存放对象成员地址的指针变量 就是指向对象成员的指针变量。

指向对象数据成员的指针
    Student s;
    string *p;
    p = &s.addr;
    cout<<*p<<endl;
    
指向对象成员函数的指针
    void(Student::*p)();
    p = &Student::display;
    Student s;
    (s.*p)();
	
	等价于:
    void(Student::*p1)() = &Student::display; 
    Student s;
    (s.*p)();

附带:

指向普通函数的指针变量
	void(*p)();
	p = fun;
	(*p)();

(3)指向当前对象的this指针
this指针是隐式的,系统自带的。
调用对象a的成员函数f:实际上是调用成员函数f时使this指针指向对象a,从而访问对象a的成员

4.常量

(1)常对象
关键词const修饰,常对象必须要有初值。

    Student const s1(1, "小明", 'M', "安徽");
    const Student s2(2, "小狗", 'M', "香港");

如果一个对象被声明为常对象,则通过该对象只能调用它的常成员函数,而不能调用该对象的普通成员函数(构造函数和析构函数除外,二者还是会由系统自动调用的)。

(2)常数据成员

    const int age;

只能通过 构造函数的参数初始化表对常数据成员进行初始化,其他函数均不能。
常对象的数据成员都是常成员

(3)常成员函数
常成员函数只能引用本类中的数据成员,不能修改。

类内声明:
    void display() const;

类外定义:
	void Student::display() const{
	    cout<<"number:"<<num<<endl;
	    cout<<"name:"<<name<<endl;
	    cout<<"sex:"<<sex<<endl;
	    cout<<"address:"<<addr<<endl;
	}

主函数中调用:
    Student s(1, 12, "小明", 'M', "安徽");
    s.display();

(4)指向对象的常指针
const型指针变量的值不能修改。因此,指向对象的常指针,即 一个常指针指向了一个对象,其值也不能改变,始终指向同一个对象,但可以改变对象的值。

    Student s(1, 12, "小明", 'M', "安徽");
    Student * const p = &s;
    p->age;
    p->display();

(5)指向常对象的指针变量
常对象:const 类名 对象名
定义指向常变量的指针变量的一般形式为:const 类型名 *指针变量名

const student stud;
const student *p;
p = &stud;

注意
(1)如果一个变量已被声明为常变量,只能用指向常变量的指针变量指向它,而不能用一般指针变量(非const类型)。
(2)指向常变量的指针变量 指向未被声明为const的变量 时,不能通过该指针变量修改该变量的值
(3)如果函数形参是指向非const变量的指针变量,则实参只能是指向非const变量的指针。形参若是指向const型变量的指针,实参既可以是指向const变量的指针,也可以是指向非const变量的指针。
在这里插入图片描述
指向常对象的指针常用于函数的形参,以保护形参指针所指向对象不被修改。
规则
当希望在调用函数时对象的值不被修改,应当把形参定义为指向常对象的指针变量,同时用对象的地址做实参(对象可以是const或非const)。
如果希望对象的值不仅在调用函数时不被修改,在程序执行过程中也不修改,应当把对象声明为const型。

(6)对象的常引用
一个变量的引用就是一个变量的别名。实质上,变量名和引用名都指向同一段内存单元。

一般引用:由于 引用作为形参,所以可以修改对应实参的值。

函数定义:
void fun(student &stud)
{
	stud.age = 20;
}

在main函数中:
  student s(1, 12, "小明", 'M', "安徽");
  fun(s);

常引用:形参为常引用时,不能修改对应实参的值。

函数定义:
void fun(const student &stud)
{
	stud.age = 20;
}

在main函数中:
  student s(1, 12, "小明", 'M', "安徽");
  fun(s);

注意:在C++中,经常使用 常指针和常引用 作为函数参数。这样既能使数据不被随意修改,保证数据安全,在调用函数时又不必简历实参的拷贝。在每次调用函数建立实参的拷贝时,都要调用复制构造函数,要有时间开销。用常指针和常引用作为函数参数,能 提高程序运行效率

const小结:

形式含义
student const stud;stud是 常对象,其值任何时候都不能修改。
void student::fun() const;fun是student类的 常成员函数,可以引用但不能修改本类的成员数据。
student * const p;p是 指向对象的常指针变量,p的值(p的指向)不能被修改(指向不能修改但是可改变存储空间的值)。
const student *p;p是 指向 student类 常对象的指针变量,p指向的类对象的值不能通过p修改。
const student &stud1 = stud;stud1是stud的 引用或别名,二者共享同意存储空间,stud的值不能被修改。

5.静态成员

“静态”可以实现数据共享。这里注意,const是用来保护数据的。
大家知道,全局变量可以实现数据共享,但是由于全局变量即可以被访问,也能被随意修改,所以全局变量安全性得不到保障,故实际工作中很少使用全局变量。
如果想 在同类中多个对象之间实现数据共享,可以用静态的数据成员,而不是用全局对象。

(1)静态数据成员
一般形式:

class student
{
public:
    static int age;
    void display();
    
private:
    static string provi;
};

注意
<1>静态数据成员是在所有对象之外单独开辟空间,它不属于类对象。一般声明类而未定义时,则类的数据成员不占用内存,只有在定义时才为数据成员分配空间。静态数据成员声明时即分配空间
<2>静态数据成员是在 程序编译时被分配空间,到 程序结束时才释放空间,一般数据成员在对象建立时分配空间,对象撤销时释放;
<3>静态数据成员 只能在类体外进行初始化,不能用参数初始化表对其初始化。如果未初始化,则编译系统自动赋予初值0
<4>静态数据成员即可以通过对象名引用,也可以通过类名来引用。若静态数据成员为私有的,则不能直接在类外引用,必须通过公用的成员函数引用。

class student
{
public:
    static int age;
    void display();
    
private:
    static string provi;
};

void student::display()
{
	...
}

int student::age = 18;
string student::provi = "北京"; //注意,这里是初始化,所以在类外可以对私有静态数据成员进行初始化,但是不能在main函数中引用。

在main函数中:
cout<<student::provi<<endl;//这是错误的,provi是私有的。

(2)静态成员函数

static void display();

<1>静态成员函数是类的一部分而不是对象的一部分。如果要在类外调用公用的静态成员函数,要用类名和域运算符“::”。

student::display();
或
student stud;
stud.display();
允许通过对象名调用静态成员函数,但静态成员函数不属于类对象。

<2>静态成员函数没有this指针,所以静态成员函数不能访问本类的非静态数据成员,前面提到:当调用一个对象的成员函数时,系统会把该对象的起始地址赋给成员函数的this指针。而静态成员函数不属于类对象,它与任何类对象都无关。
<3>在C++中,静态成员函数主要用来访问静态成员数据,而不访问非静态成员数据。应该养成好习惯:只用静态成员函数引用静态数据成员,而不引用非静态数据成员

注意区别const和static:const用来保护数据,static是为了共享数据。

6.对象的动态建立和释放

new动态建立对象,delete撤销对象。

student *p;
p = new student;

注意:用new运算符动态地分配内存后,将返回一个指向新对象的指针,即所分配的内存空间的起始地址。可 定义一个指向本类对象的指针变量来存放该地址

7.对象的赋值和复制

(1)对象赋值
同类对象 之间可以相互对 数据成员 进行赋值。

student stud,stud1;
stud = stud1;

(2)对象复制
用已有的对象stud克隆一个stud1,背后是通过调用 复制构造函数 实现的。

对象复制一般形式:
student stud1(stud);
或
student stud1 = stud;

复制构造函数:
student::student(const student &stud)
{
	num = stud.num;
	age = stud.age;
	...
}

注意
<1>对象赋值是已存在一个对象,然后对其赋值;对象复制是创建一个和已有对象完全一样的新对象。

<2>复制构造函数

一般形式:
类名(类名 &对象名);

student(student &stud);

普通构造函数在建立对象时调用,复制构造函数在复制对象时调用。

复制构造函数的调用(全为系统自动实现,了解即可):

  • 通过复制建立新对象;
  • 当函数参数为类对象时。在调用函数时需要将实参对象完整地传递给形参,也就是建立一个实参的拷贝,这时是通过复制构造函数实现的。
  • 函数返回值是类的对象。由于函数调用结束时,对象会被释放,所以return返回的是复制的对象。

8.友元

友元(friend)包括友元函数和友元类。友元用来访问与其有友好关系的类中的私有成员

(1)将普通函数声明为友元函数

class student
{
public:
    int age;
    friend void display(student &);
    
private:
    string provi;
};

//void student::display(student &s)//类student的成员函数display定义
void display(student &s)//display函数不是类student的成员函数
{
	cout<<s.age<<endl;
	cout<<s.provi<<endl;
}

注意:display函数不是类student的成员函数,没有this指针,不能默认引用student类的数据成员,必须指定要访问的对象。

(2)友元成员函数
友元函数还可以是另一个类中的成员函数。

class Date;
class Time
{
public:
	void display(Date &);
	...
}

class Date
{
public:
	friend void Time::display(Date &);
}

注意:
<1>一个函数(包括普通函数和成员函数)可以被多个类声明为“朋友”,这样就可以引用多个类中的私有数据
<2>C++允许对类作“提前引用”的声明,即在正式声明一个类之前,先声明一个类名,表示此类在稍后声明。

(3)友元类
在类A的定义体中,声明类B为其友元类。

class B;
class A
{
	friend B;
}

友元类B的所有函数都是类A的友元函数,可以访问类A中的所有成员。

注意
<1>友元关系是单向而不是双向;
<2>友元的关系不能传递。

9.类模板

有两个或多个类,其功能是相同的,仅仅是数据类型不同

template <class numtype>
class Compare
{
public:
	Compare(numtype a, numtype b)
	{
		x = a;
		y = b
	}

	numtype max()
	{
		return (x>y)?x:y;
	}

	numtype min()
	{
		return (x<y)?x:y;
	}

private:
	numtype x,y;
}

类模板是类的抽象,类是类模板的实例。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值