类和对象(初步探索)

1 类的声明 对象的定义

其中:成员访问限定符号:private,public和protected

class student
{
private://私有部分
	int num;
	char name[20];
	char sex;
	protected//在类内视为public在类外视为private,这一点在后面的
public://公用部分
	void display()
	{
		cout << "num:" << num << endl;
	}
};
student stud1, stud2;//定义student类的两个对象

2 类的成员函数【和inline内置的】

两种定义方法:
1)在类体中(如上面的例子)
2)在类体中声明,在类外定义
其中:作用域限定符(::)表示函数是属于哪个类当中的
同样遵循先声明后定义的原则

class student
{
private://私有部分
	int num;
	char name[20];
	char sex;
public://公用部分
	void display();//函数声明
};
void student::display()
{
		cout << "num:" << num << endl;
}
student stud1, stud2;//定义student类的两个对象

提示:一个类中至少一个公用的成员函数,作为对外的接口

(2)inline 内置的(将调用的函数代码嵌入到程序的调用点进而减小调用成员函数的时间开销)

其中类内的函数默认是内置的
注意:所以在类外定义的函数需要添加inline声明

inline void student::display()

(3)成员函数的储存方式

类中成员函数的代码储存在一段空间中,在调用各对象的函数时,都调用这个公用的函数代码(借助this指针)
因而,在sizeof(student)求student类对象占用的字节数的时候,只是该类对象数据成员占用的储存空间而不包含代码占用的储存空间
说明:
(1)不论成员函数是否在类外定义,都不占用对象的储存空间
(2)不管是不是inline成员函数都不占用对象的储存空间(inline只影响程序的执行效率)
在这里插入图片描述
测试发现类和对象长度相同。

3 对象成员的引用

方式:
(1)对象名.成员名
类外只能访问public成员而不能private成员
(2)指针
前提:指针有指向

student stud1, * s;
s = &stud1;
cout << s->num;
cout << (*s).num;
cout << stud1.num;

(3)引用

student stud1, & s=stud1;
cout << s.num;

4 类的封装性和信息隐蔽

私有和protected成员在类外不可访问

5 构造函数

e.g.

在#include<iostream>

#include <string>

using namespace std;

class Worker {

private:

    char name[15];

    int age;

    float pay;

public:

    Worker() {

        name[0] = '\0';

        age=0;

       flag=0;

    }//默认构造函数

    Worker(const char*n, int a, float p) :age(a),pay(n)  {strcpy(name,n); }//带参构造函数
    //注意:⚠️使用指针常量

};

int main() {

    Worker x;

    Worker x2("Tom", 20, 2000);

    return 0;

}

在这里插入图片描述
什么都不写的时候,有默认构造函数,但是是空的,什么都不干
在这里插入图片描述
1)默认构造函数&带形参的构造函数

#include<iostream>
using namespace std;
class Time
	{
	private:
		int hour;
		int min;
		int sec;
	public://构造函数没有返回值类型
		Time(int a, int b, int c);//构造函数
		Time();//默认构造函数
		void show();
	};
Time::Time()
{
	hour=0;
	min = 0;
	sec = 0;
}
Time::Time(int a, int b, int c) 
{
	hour = a;
	min = b;
	sec= c;
}
int main()
{
	Time t1;//隐含调用默认构造函数
	t1.show();
	Time t2(1, 2, 3);//隐含调用构造函数,初始值为实参
	t2.show();


}
void Time::show()
{
	cout << hour << ' ' << min << ' ' << sec << endl;
}

在这里插入图片描述
2)带默认形参值的构造函数

Time::Time(int a=0, int b=0, int c=0)
{
	hour = a;
	min = b;
	sec = c;
}

注:
1)带默认形参值的构造函数不能和构造函数进行重载
在这里插入图片描述
2)在所有参数添加默认值时,这样和默认构造函数的作用一样,导致函数调用不明确
在这里插入图片描述
3)默认值写在声明中,不写在定义中
原因:
1)两处都写提示重复定义
在这里插入图片描述
2)只写在定义中,但是调用的时候按照函数的声明进行重载函数的选择
在这里插入图片描述
注意:
默认值从右边开始写起,中间不能有跳跃
在这里插入图片描述
e.g.2
在这里插入图片描述
3)参数初始化表
在这里插入图片描述

4)调用次数
e.g.1

#include<iostream>
using namespace std;
int n = 0;
class Time
{
private:
	int hour;
	int min;
	int sec;
public://构造函数没有返回值类型
	Time();
	void show();
};
int main()
{
	Time a1;
	Time c[10];
	Time* q;
	cout << n<<endl;
}
void Time::show()
{
	cout << hour << ' ' << min << ' ' << sec << endl;
}
Time::Time()
{
	hour =0;
	min = 0;
	sec = 0;
	n++;
}


输出n==11
没有生成新的调用的情况:
1 指针不进行初始化
2 引用不进行初始化

e.g.2

class AA {

   int a,b;

public:

   AA() { cout << "1" << endl; }

   AA(int c, int d = 0) { cout << "2" << endl; }

};

void main()

{
                  //输出
  AA a;           //1                   //

  AA b{1};        //2                  //第2空

  AA b2 = {2,2};  //2              //第3空

  AA* p = &a;     //空,也就是说不进行初始化              

  AA& r = a;      //空,没有构造新的对象

  a = 2;          //**2,单独的int类型不满足等号,所以先是创建一个对象用2调用含参构造函数,再利用重载的等号运算符赋值给a的,在后面可以使用重载等号来体验**

}

变形:

 #include<iostream>
using namespace std;
class AA {

	int a, b;

public:

	AA():a(0),b(0) { cout << "默认" << endl; }

	AA(int c, int d = 0):a(c),b(d) { cout << "带参" <<' '<<a<<' '<<b<< endl; }

};
int main()
{
	AA* p3[3] = { new AA(), new AA[2]{2},NULL }; //空指针不调用构造函数
}

输出:
默认
带参 2 0
默认
其中NULL不调用构造函数

拷贝构造函数

作用:用对象初始化对象
通常函数的参数是:对象的常引用
区分:
编译器默认的拷贝构造函数也实现一对一赋值,但是是浅拷贝
尤其是遇到数组或字符串或指针的情况,为了实现创建新的空间将内容放入,即实现深拷贝
应该自己写拷贝构造函数

class AA {

   int a,b;

public:

   AA() { cout << "1" << endl; }

   AA(int c, int d = 0) { cout << "2" << endl; }

   AA(const AA& a) { cout << "c" << endl; }

};

//已有函数:

AA f1(AA t){ return t; }

AA f1(){AA t;  return t; }


//如下程序会打印什么信息:

AA a(2);

AA b = f1(a);  //第二步  

b = f1(); 

输出
//第一步
2
//第二步:f1函数中的形参需要初始化:return语句,形参作为局部变量生命周期结束,需要创建新的变量将数值传递出去
c
c
//第三步
1
c//和上面的解释同理

析构函数

)无形参,也不能重载
)先调用构造函数的最后调用析构函数,最后调用构造函数的最先调用析构函数
(只有当程序中delete a;说明调用的时候才会反顺序)

#include<iostream>//认为delete时候,才不按照顺序进行析构
using namespace std;
class AA {

public:

    AA(int i, int j) { A = i; B = j;  cout << "Constructor\n"; }

    AA(AA& obj) { A = obj.A + 1; B = obj.B + 2;  cout << "Copy Constructor\n"; }

    ~AA() { cout << "Destructor for "; print(); }

    void print() { cout << "A=" << A << ",B=" << B << endl; }

private:

    int A, B;

};

int main() {

    AA a1(2, 3);

    AA a2(a1);

        a2.print();

    AA* pa = new AA(5, 6);

    pa->print();

    delete pa;

    cout << "End\n";

    return 0;

}

输出:
Constructor
Copy Constructor
A=3,B=5
Constructor
A=5,B=6
Destructor for A=5,B=6
End
Destructor for A=3,B=5
Destructor for A=2,B=3

class AA {

private:

    int a;

public:

    AA() { cout << "Cons:1" << endl; }

    AA(int c) { cout << "Cons:2" << endl; }

    ~AA(){ cout << "Des" << endl; }

};

void main()

{

    AA a;输出Cons:1和Des,调用一次构造函数就有一次析构函数

   AA* p = &a;空

   AA& r = a;空

   AA b = 1;输出Cons:2和Des

}

最后输出:
Cons:1
Cons:2
Des,AA b = 1的
Des,AA a的
e.g.1

class Person

{

private:

	char* name;

	int age;

public:

	Person();

	Person(const char*, int);

	Person(const Person&);

	~Person();

	void show() { cout << name << ", " << age << endl; }

};

Person::Person()

{

	name = new char[1];

	name[0] = '\0';

	age = 0;

}
Person::Person(const char*a, int n)
{
	int size = strlen(a) + 1;
	name = new char[size];
	strcpy_s(name,strlen(a)+1,a);
	age = n;
}
Person::Person(const Person& a)
{
	int size = strlen(a.name) + 1;
	name = new char[size];
	strcpy_s(name,strlen(a.name)+1,a.name);
	age = a.age;
}
Person::~Person()
{
	delete[]name;
}
int main() {

	Person a;

	Person b("tom", 4);

	Person c = b;

	a.show();

	b.show();

	c.show();

	return 0;

}

e.g.2

#include<iostream>
using namespace std;
class Part  //部件类

{

public:

    Part() { val = 0; cout << "1"; }

    Part(int i)

    {

        val = i;
        if (val == 1)
            cout << 2;
        else if (val == 2)
            cout << 3;
    }

    ~Part() {
        if (val == 1) cout << 4;
        if (val == 2) cout << 5;
        if(val==0) cout << 6;
    }

private:

    int val;

};

class Whole

{

public:

    Whole() { date = 0; cout << 7; }

    Whole(int i, int j, int k) :two(j), one(i), date(k)

    {

        cout << 8;

    }

    ~Whole() { cout << 9; }

private:

    Part one;

    Part two;

    int date;

};

int main()

{

    Whole a;

    Whole b(1, 2, 3);

}

输出:117238954966
说明:参数初始化表中初始化的顺序看的是声明中的顺序

组合

注意:
数据成员中如果是另一个类,则在构造的过程中也会调用另一个类的构造函数(析构函数的调用正好相反)
在初始化类的数据成员时,成员类型是一个类的要先于其他成员
初始化类为类型的数据成员:
1)参数初始化表(函数体外部)
2)函数体内部
》使用构造函数:
不可行,根据上面提到的,在执行到函数体的时候成员已经被初始化,不能初始化两次
在这里插入图片描述

》使用等号重载:
可行
参数初始化表中,不是按照写的顺序,而是类中声明的顺序调用构造函数

#include<iostream>
using namespace std;
class Date {

public:

	Date(int a = 0, int b = 0, int c = 0) { year = a; month = b; day = c; cout << "Date cons." << endl; }

	Date(const Date& d) { year = d.year; month = d.month; day = d.day; cout << "Date copy cons." << endl; }

	~Date() { cout << "Date destroyed." << endl; }

	void show() { cout << year << ", " << month << ", " << day << endl; }

private:

	int year, month, day;

};
class Employee
{
private:
    char name[20];
    Date birthday;
public:
    Employee() :birthday() { name[0] = '\0'; };
    Employee(const char* a, int i, int j, int k); 
    Employee(const char* a, const Date& t);
    void show();
};
Employee::Employee(const char* a, int i, int j, int k)
{
    strcpy_s(name, strlen(a) + 1, a);
    birthday={i, j, k};//注意:在运行到函数体部分时birthday已经构造完成,所以不能在函数体中使用birthday(i,j,k),而使用等号重载
}
Employee::Employee(const char* a, const Date& t)
{
    strcpy_s(name, strlen(a) + 1, a);
    birthday = t;
}
void Employee::show()
{
    cout << name << endl;
    birthday.show();
}
int main()

{

    Employee a("Tom", 2020, 11, 1);

    a.show();

    Date t;

    Employee b("Tom", t);

    b.show();

    Employee c;

    c.show();
}

输出:
Date cons.//构造birthday
Date cons.//构造{1,2,3}的对象,使用等号赋值重载
Date destroyed.//解除{1,2,3}对象(因为birthday已经构造完成不需要拷贝构造)
Tom
2020, 11, 1
Date cons.
Date cons.
Tom
0, 0, 0
Date cons.

0, 0, 0
Date destroyed.
Date destroyed.
Date destroyed.
Date destroyed.

前向引用声明

像函数一样 在使用类之前先声明
对于指针或函数形参的情况(不涉及变量的空间),可以不用前向声明
但如果成员直接是类的对象的时候,因为在构造时涉及成员空间的开辟,但是因为类没有完全写出,所以报错

静态成员

静态数据成员:所有的类都使用一种,被所有成员共用
注意:静态数据成员需要初始化再声明对象
e.g.1

#include<iostream>
using namespace std;
class A
{
	static int a;//静态数据成员
public:
	A(int m) { a = m;}
	A() { a = 0; }
	void show() { cout << a << endl; }
};
int A::a = 0;//静态数据成员要初始化
int main()
{
	A a,b(2);
	a.show();
	b.show();
}

输出
2
2

若将main函数更改为

int main()
{
	A a;
	a.show();
	A b(2);
	b.show();
}

输出:0 2
静态成员函数:被类名调用
静态函数只能使用静态的数据成员或已知对象的成员。
可以用类名(而不是对象名)调用静态成员函数

#include<iostream>
using namespace std;
class A
{
	static int num;//静态数据成员
	int x;
public:
	A(int m = 0) { num++; x = m; }
	~A() { num--; }
	static int NUM() { return num; }
};
int A::num = 0;//静态数据成员要初始化
int main()
{
	A a[10];
	cout << A::NUM() << endl;
}

输出:10
应用:用静态数据成员和函数可以帮助类实现数组的功能

常成员函数&&常对象

1)常函数的概念

const int f();

表示返回值为常量

int const f();

表示常函数
e.g.2

#include<iostream>
using namespace std;
class A
{
	const int x;常数据成员
public:
	A(int m = 0) :x(m){} 常数据成员只能使用初始化表进行初始化
	构造函数先构造常数据成员,并且先执行初始化表再执行函数体
	void print()
	{
		cout <<"非"<< x << endl;
	}
	void print()const
	{
		cout << "常" <<x<< endl;
	}
	void show()
	{
		cout << x << endl;
	}
};
int main()
{
	A a(2);
	const A b(3);//常对象
	a.print();
	b.print();//调用的是常成员函数的print
}

由此可得:
1 同名函数的重载可以用const区分
2 常成员数据的初始化只能在初始化表中(在大括号中的话,常成员已经被构建,相当于再次初始化)
3
在这里插入图片描述
在这里插入图片描述
如图所示,常对象只能调用常成员函数
e.g.

#include<iostream>
#include<string>
#include<string.h>
using namespace std;
class A

{

    int a;

public:

    A(int t = 0) { a = t; }

    void show() const { cout << a; }

    int get(){ return a; }

};
int main()
{
    const A a;//编译出错:const成员只使用const成员函数
    cout << a.get();
}

⚠️:用const可以区分函数重载,并且可以被常对象或非常对象调用。
同时在拷贝构造函数中最好使用常引用。

》常数据成员の初始化:
1 参数初始化表
A::A():id(10)
{ score=0;}
常数据成员的初始化不能写在函数体里:函数体里使用赋值语句,参数初始化表初始化变量。
2 声明同时赋值

(this)类的指针

1 为避免数据成员被屏蔽,用this->指向成员。
2 在return自己对象的情况下,(*this)

⚠️:
默认拷贝构造函数在涉及字符指针时是指针直接赋值(浅拷贝),那么,在delete一个之后,另一个指针的内存空间也被撤销,则最后delete(析构)的时候报错

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值