面向对象编程Coming!!!
目录
1构造函数与析构函数
构造函数与类同名且不返回任何值,其总是在创建对象时被调用,适用于将类成员进行初始化 。
class Car
{
private:
string name;
int price;
public:
Car();
};
Car::Car()
{
cout<<"A car has been constructed!";
price = 0;
}
1.1重载构造函数
构造函数可以进行重载,一个类可以包含多个构造函数,用途如下:
class Car
{
private:
string name;
int price;
public:
Car();
Car(string carName);
};
Car::Car()
{
cout<<"A car has been constructed!";
price = 0;
}
Car::Car(string carName)
{
cout<<"A car has been constructed!";
name = carName;
price = 0;
}
int main()
{
Car car1;
Car car2("Tesla");
}
对于没有默认构造函数的类(也就是构造函数是我们自己写的),例如注释掉上面代码段的Car(),那么创建car时就必须要提供车的姓名。
注:默认构造函数时调用时可以不提供参数的构造函数,但不接受参数的构造函数并不一定是默认构造函数,如带有参数值的构造函数。
Car::Car(string carName, int carPrice = 250000)
{
cout<<"A car has been constructed!";
name = carName;
price = carPrice;
}
对于上面这个类的构造函数,实例化该类时可以用Car("Tesla") || Car("Tesla", 3500000)。
1.2析构函数
析构函数和类同名,就是前面多了一个~。每当对象不再在作用域内(例如函数结束时等)或通过delete被删除进而被销毁时,析构函数都将被调用。
析构函数通常用来重置变量或释放动态分配的内存和其他资源。
class Human
{
private:
/* data */
string name;
int age;
char* buffer;
public:
Human(const char* initString);
Human(string humanName, int humanAge);
~Human();
int GetLength()
{
return strlen(buffer);
}
};
Human::Human(const char* initString)
{
buffer = NULL;
if(initString != NULL)
{
buffer = new char [strlen(initString) + 1];
strcpy(buffer, initString);
}
}
Human::~Human()//析构函数不能重载,每个类只能有一个析构函数
{
cout<<"A human has been destroied."<<endl;
delete[] buffer;
}
注意析构函数不能重载,每个类只能有一个析构函数。
2复制构造函数
2.1浅复制
下面的代码承接上面那一段:
其中myStr是实参对象myHuman的复制,它也复制了实参对象的指针成员,由于二进制复制不复制指向的内存单元,故其不复制实参对象的buffer指针指向的缓冲区,因此两个对象的buffer指针指向的是同一块内存区域。
void UseHumanString(Human myStr)//myStr是实参的复制,会复制实参的指针成员,但不复制指针指向的缓冲区。
{
cout<<"String buffer's length is "<<myStr.GetLength()<<endl;
}//函数结束,myStr不在作用域内,将调用析构函数,释放分配给buffer的内存,这将导致main中的对象myHuman1的buffer指针指向的内存无效。
int main()
{
Human myHuman1("My name is String Class!");
Human myHuman2("eva", 12);
UseHumanString(myHuman1);
return 0;
}//main结束,myHuman1不在作用域,调用析构函数,这时会对不再有效的内存单元buffer再次调用delete,从而导致bug。
在UseHumanString函数结束和main函数结束时都会对buffer指针调用delete,从而导致程序崩溃,详细解释看上面代码的注释。
2.2使用复制构造函数确保深复制
复制构造函数。每当对象被复制时,编译器都会调用复制构造函数。
复制构造函数接受一个以引用方式传入的当前类的对象作为参数。这个参数是原对象的别名。
在以上代码的基础上加一个复制构造函数:
Human::Human(const Human& copySource)
{
buffer = NULL;
if(copySource.buffer != NULL)
{
buffer = new char[strlen(copySource.buffer) + 1];
strcpy(buffer, copySource.buffer);
}
}
由此,myHuman1的buffer是在构造函数中使用new分配的内存,myStr的buffer是在复制构造函数中分配的内存,两者指向不同的内存单元,避免了上面浅复制造成的问题。
注:①在复制构造函数中使用const,可以确保复制构造函数不会修改指向的源对象;
②复制构造函数的参数必须按引用传递,否则复制构造函数将不断调用自己;
③当类包含原始指针成员时(如char* ),务必编写复制构造函数和复制构造运算符;
④除非万不得已,不要将类成员声明为原始指针。
复制构造函数起作用的三种情况:
①用一个对象去初始化同类的另一个对象
Human human1(human2); Human human1 = human2;
②如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用
如上面的例子所示;
③如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数被调用
Human myChild()//返回的值是Human的对象,那么这个对象将用复制构造函数初始化。
2.3移动构造函数
没太看懂书上是什么意思...等12章看详细讲解。
3.构造函数和析构函数的其他用途
3.1不允许复制的类
声明一个private的复制构造函数,这样就可以保证类的对象不可以被复制。
3.2只有一个实例的单例类
将复制构造函数设置成private,同时利用静态函数获取类的实例。
class Present
{
private:
string name;
Present(){};//私有的默认构造函数
Present(const Present&);//私有的复制构造函数
const Present& operator=(const Present& );
public:
static Present& GetInstance()
{
//静态object只被创建一次
static Present onlyInstance;
return onlyInstance;
}
string GetName()
{
return name;
}
void SetName(string InputName)
{
name = InputName;
}
};
int main()
{
Present& onlyPresent = Present::GetInstance();
onlyPresent.SetName("Xu xiaoxiao");
cout<<"Name of the present: "<<onlyPresent.GetName()<<endl;
Present& second = Present::GetInstance();
second.SetName("Fake xuxiaoxiao");//有点像给唯一的present改了个名字
cout<<"Name of the present: "<<onlyPresent.GetName()<<endl;
cout<<"Name of the fake present: "<<second.GetName()<<endl;
return 0;
}
结果:
4.this指针
关键字this包含当前对象的地址,其值为&object。当在类成员中调用其他成员方法时,编译器将隐式的传递this指针。P173/188
5.声明友元
使用关键字friend。类的友元可以访问该类的私有变量,友元可以是类也可以是函数。
class Human
{
private:
/* data */
friend void DisplayAge(const Human& person);//声明DisplayAge函数为友元,该函数可以访问该类的私有数据和方法
friend class Utility;//声明Utility类为友元,Utility可以访问该类的私有数据和方法
string name;
int age;
public:
Human(string humanName, int humanAge);
};
Human::Human(string humanName, int humanAge)
{
name = humanName;
age = humanAge;
}
void DisplayAge(const Human& person)//传递引用
{
cout<<person.age<<endl;
}
class Utility
{
public:
static void DisplayTheAge(const Human& person)
{
cout<<person.age<<endl;
}
};
int main()
{
Human man1("Rose", 18);
cout<<"Access to Private member age of Rose by function, the age is: ";
DisplayAge(man1);
cout<<"Access to Private member age of Rose by class, the age is: ";
Utility age1;
age1.DisplayTheAge(man1);
//or Utility::DisplayTheAge(man1);
return 0;
}
6.共用体
共用体是一种特殊的类,每次只有一个非静态数据成员处于活动状态。成员默认为共有的。