构造函数:
含义:该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作。
作用:初始化对象的数据成员。
个人理解:类的构造函数是类的一种特殊成员函数,他会在每次创建类的新对象时执行。
例(无参):
#include <iostream>
using namespace std;
class Line
{
public:
void setLength(int len);
int getLength(void);
Line(); // 这是构造函数
private:
double length;
};
// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;
}
void Line::setLength(int len)
{
length = len;
}
int Line::getLength(void)
{
return length;
}
// 程序的主函数
int main()
{
Line line;
// 设置长度
line.setLength(6);
cout << "Length of line : " << line.getLength() << endl;
system("pause");
return 0;
}
例(有参):
#include <iostream>
using namespace std;
class Line
{
public:
void setLength(int len);
int getLength(void);
Line(int line); // 这是构造函数
private:
int length;
};
// 成员函数定义,包括构造函数
Line::Line(int line)
{
cout << "Object is being created" << endl;
length = line;
}
void Line::setLength(int len)
{
length = len;
}
int Line::getLength(void)
{
return length;
}
// 程序的主函数
int main()
{
Line line(60);
// 设置长度
// 获取默认设置的长度
cout << "Length of line : " << line.getLength() << endl;
// 再次设置长度
line.setLength(6);
cout << "Length of line : " << line.getLength() << endl;
system("pause");
return 0;
}
复制构造函数:
是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新的对象并用一个同类型的对象对他进行初始化的时候,将显示使用复制构造函数。当该类型的对象传递给函数或者从函数返回该类型的对象时,将隐式的调用复制构造函数。
c++支持两种初始化形式A为构造函数:
- 直接初始化: A x(2);
- 复制初始化: A Y = X;
什么情况下可以使用复制构造函数? - 一个对象以值传递的方式进入到函数体。
- 一个对象以值传递的方式从函数返回。
- 一个对象需要通过另一个对象进行初始化。
必须使用复制构造函数的地方:
只包含类类型成员或者内置类型(不是指针类型)成员的类,无须显式的定义复制构造函数也可以复制。有的类它的数据成员有一个指针,或者是成员表示在构造函数中分配其他资源。
示例:
#include<iostream>
#include<windows.h>
using namespace std;
class Date
{
public:
Date(int year, int month, int day)//构造函数
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)//拷贝构造函数,必须传引用
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Display()const
{
cout << _year << "_" << _month << "_" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date date1(2018, 8, 29);
//下面两种调用拷贝构造函数是等价的。
Date date2(date1);
Date date3 = date1;
cout << "date1" << endl;
date1.Display();
cout << "date2" << endl;
date2.Display();
cout << "date3" << endl;
date3.Display();
system("pause");
return 0;
}
深浅拷贝:
深拷贝:对于对象中动态成员,不能简单地赋值,应该重新分配空间。
示例:
#include <iostream>
#include <string.h>
#include<windows.h>
#pragma
using namespace std;
class String
{
public:
String(const char* str = "")//构造函数
{
cout << "String" << endl;
if (NULL == str)
str = "";
_str = new char[strlen(str) + 1];
memcpy(_str, str, strlen(str) + 1);
}
String(const String& s)//复制构造函数
:_str(NULL)
{
cout << "copy String" << endl;
String strTemp(s._str);
swap(_str, strTemp._str);
}
~String()//析构函数
{
if (_str)//销毁,若为空,则不用销毁
{
cout << "~String" << endl;
delete[] _str;//注意new[],delete[]的匹配使用
}
}
private:
char* _str;
};
void Test()
{
String _str1("hello world");
String _str2(_str1);//_str2需要调用String类的拷贝构造函数来创建,由于未显示定义,因此使用默认合成的拷贝构造函数
}
int main()
{
Test();
system("pause");
return 0;
}
浅拷贝:
将一个指针指向另一个指针,这样就会使两个指针指向同一块空间,这就是浅拷贝。
示例:
#include <iostream>
#include <string.h>
#include<windows.h>
using namespace std;
class String
{
public:
String(const char* str = "")//构造函数
{
cout << "String" << endl;
if (NULL==str)//判断指针是否是空指针
{
str = new char[1];
}
else
{
_str = (new char[strlen(str) + 1]);
memcpy(_str, str, strlen(str) + 1);
}
}
~String()//析构函数
{
cout << "~String" << endl;
if (_str)
{
delete[] _str;//注意new[],delete[]的匹配使用
}
}
private:
char* _str;
};
void Test()
{
String _str1("hello world");
String _str2(_str1);//_str2需要调用String类的拷贝构造函数来创建,由于未显示定义,因此使用默认合成的拷贝构造函数
}
int main()
{
Test();
system("pause");
return 0;
}
课后习题:
7.16:在类的定义中对于访问说明符出现的位置和次数有限制吗?如果有是什么??什么样的成员应该定义在public说明符之后??什么样到成员应该定义在private说明符之后??
1.没有限制。
2.因为public之后的成员可以在整个程序中被调用,所以一般来说,作为接口的一部分,构造函数和一部分成员函数应该定义在public之后。
3.private之后的成员只能在类中的成员函数访问,但是不能被使用该类的代码访问。所以说数据成员和作为实现部分到函数应该跟在private后面。
7.17:使用class和struct时有区别吗?如果有,是什么?
有。
1.默认继承权限:class默认继承权限是private但是站struct则是public。
2.成员默认访问权限:class的成员默认是private权限,struct的成员默认则是public权限。
3.除了以上两点,两者在语法上没有任何区别。
析构函数:
类的析构函数是一种特殊的成员函数。他会在每次删除所创建的对象时执行。
个人理解:
析构函数的名称和类完全相同,只是在前面加了一个~符号。它不存在任何的返回值,也不能携带参数。析构函数有助于在跳出程序(比如关闭文件、释放资源等)前释放资源。
例:
#include <iostream>
using namespace std;
class Line
{
public:
void setLength(double len);
double getLength(void);
Line(); // 这是构造函数声明
~Line(); // 这是析构函数声明
private:
double length;
};
// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;
}
Line::~Line(void)
{
cout << "Object is being deleted" << endl;
}
void Line::setLength(double len)
{
length = len;
}
double Line::getLength(void)
{
return length;
}
// 程序的主函数
int main()
{
Line line;
// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() << endl;
//system("pause");//不会析构
return 0;
}
友元:
- 友元是一种说明在类体外非成员函数,友元不是成员函数,但是它可以访问类中的私有成员。
- 作用:提高程序的运行效率,但是它破坏了类的封装性和隐藏性,使得非成员函数可以访问类中的私有成员。
友元函数: - 可以直接访问类的私有成员。
- 它是定义在类外部的普通函数,不属于任何类。
- 需要在类的内部声明,声明时需加friend关键字。
示例:
#include <iostream>
using namespace std;
class A
{
public:
friend void set_show(int x, A &a);//友元函数声明
friend void T(A &a);
int i1 = 0;
private:
int date;
int i = 001;
};
void set_show(int x, A &a) //友元函数定义,访问类A中的成员
{
a.date = x;
cout << a.date << endl;
}
void T(A &a)
{
int x;
x = a.i1;
cout << x << endl;
cout << a.i << endl;
}
int main(void)
{
class A a;
set_show(90, a);
T(a);
system("pause");
return 0;
}
友元类:
友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一个类的友元类。
注意事项:
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A到友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明。
示例:
#include <iostream>
using namespace std;
class A
{
public:
friend class C;//友元类的声明
private:
int data;
};
class C //友元类的定义,为了访问类A中的成员
{
public:
void set_show(int x, A &a)
{
a.data = x;
cout << a.data << endl;
}
};
int main(void)
{
class A a;
class C c;
c.set_show(80, a);
system("pause");
return 0;
}
友元成员函数:
使类B中的成员函数成为类A中的友元函数,这样类B的该成员函数 就可以访问类A的所有成员了。
当用到友元函数时,需要注意友元声明和友元定义之间的相互依赖,在该例子中,类B必须先定义,否则类A就不能将一个B到函数指定为友元。然而,只有在定义了类A以后,才能定义类B的该成员函数。更一般的讲,必须先定义包含成员函数的类,才能将成员函数设置为友元。另一方面,不必预先声明类和非成员函数来将他们设为友元。、
示例:
#include<iostream>
using namespace std;
class A;//每当用到友元函数时,需注意友元声明与友元定义之间的互相依赖。这是类A的声明。
class B
{
public:
void set_show(int x, A &a);//该函数是类A的友元函数
};
class A {
public:
friend void B::set_show(int x, A &a);//该函数是友元函数的声明
private:
int data;
void show() {
cout << data << endl;
}
};
void B::set_show(int x,A &a)//只有在定义类A后才能定义该函数,毕竟它被设为友元是为了访问类A到的成员
{
a.data = x;
cout << a.data << endl;
}
int main(void)
{
class A a;
class B b;
b.set_show(66, a);
system("pause");
return 0;
}
友元小结(部分通过百度):
在需要允许某些特定的非成员函数访问一个类的私有成员(及受保护成员),而同时仍阻止 一般的访问的情况下,友元是可用的。
优点:
- 可以灵活地实现需要访问若干类的私有或受保护的成员才能完成的任务
- 便于与其他不支持类概念到语言(如C语言、汇编等)进行混合编程
- 通过使用友元函数重载可以更自然地使用C++语言的IO流库
缺点:
一个类将对非公有成员的访问权限授予其他函数或者类,会破坏该类的封装性,降低该类的可靠性和可维护性。
个人类小结:
- 和非成员函数一样,成员函数也可以被重载。
- 每个类定义了唯一的类型。对于两个类来说,即使他们到成员完全一样,他们也是属于两个不同的类型。例:
struct A
{
Int a;
Int b;
}
struct B
{
Int a;
Int b;
}
A obj1;
B obj2=obj1; //错误,obj1和obj2的类型不同,所以不能直接赋值。
- 我们可以直接把类名作为类型的名字使用,从而直接指向类类型。或者,我们也可以把类名跟在关键字class或者struct之后,例子:
Sale a; //默认初始化Sale类型的对象
Class Sale a; //与上面等价 - 类的声明和定义可以分离开进行。
- 类允许我们为自己的应用定义新的类型,从而使得程序更加简洁且于修改。