您的位置:首页 > 编程语言 > C语言/C++

C++基础知识(二)C++类基础

2018-01-30 14:29 429 查看

目录

1.类的定义

2.类的两种实现方式

3.对象的定义和对象成员的使用

4.构造函数和析构函数

5.堆对象

6.public,private,protected的使用

7.特殊成员函数

8.自定义String类

1.类的定义

类是一种抽象数据类型,它是将不同类型的数据和与这些数据相关的操作封装在一起的集合体。因此,类的定义一般包含数据成员和成员函数。

2.类的两种实现方式

类的成员函数的实现有两种方式:

1.在类中边定义边实现

2.在类中定义在类外实现

class Date
{
public:
void setDate(int y, int m, int d);
int isLeapYear();//只有声明,函数体定义在类外
void print(){
cout << year << "." << month << "." << day <<endl;
}//函数体写在类定义中
private:
int year, month, day;
};

void Date::setDate(int y, int m, int d){
year = y;
month = m;
day = d;
}

int Date::isLeapYear(){
return (year % 4 == 0 && year % 100 != 0)||(year % 400 ==0);
}


3.对象的定义和对象成员的使用

类定义完后要用通过对象才能使用,对象是类的实例。对象的定义格式:

<类名> <对象名表>

对象名表可以一个或多个对象名,多个时用逗号分隔。可以是一般的对象名,还可以是指向对象的指针和对象数组。

Date date1, date2, *Pdate, date[31];


其中,date1,date2是一般对象名,Pdate是指向对象的指针,date是对象数组的数组名,有31个元素,每个元素都是一个Date类的对象。

一个对象的成员就是该对象的类所定义的成员,一般对象表示如下:

<对象名>.<成员名>或

<对象名>.<成员名>(<参数列表>)

其中”.”是一个运算符,表示对象的成员。

int year = date1.year;
date1.setDate(2018, 1, 30);


 当使用的是对象指针时,成员使用如下:

int year = Pdate->year;
Pdate -> setDate(2018, 1, 30);


也可以使用如下表示方法:

int year = (*Pdate).year;
(*Pdate).setDate(2018, 1, 30);


定义类时的注意事项

在类体中不允许对定义的数据成员进行初始化。

class Date{
public:
...
private:
int year(1998),month(4),day(9);
};//在C++11中才允许对非静态成员初始化


另外,类中的数据成员类型可以是任意的,可以是基本类型也可以是对象,但是自身类的对象是不可以的,而自身类的指针或引用是可以的。

4.构造函数和析构函数

构造函数和析构函数是在类体中说明的两种特殊的成员函数。

构造函数:在创建对象时,使用给定的值将对象初始化

析构函数:在释放一个对象时,在对象删除前,用它来做一些清理工作,与构造函数功能正好相反。

但这两个函数不是必须自己定义的,如果不定义,C++会自动生成空实现的构造函数和析构函数。

class Date1
{
public:
Date1(int y, int m, int d);//构造函数
~Date1();//析构函数
private:
int year,month,day;
};
Date1::Date1(int y, int m, int d){
year = y;
month = m;
day = d;
cout << "Constructor called.\n";
}
Date1::~Date1(){
cout << "Destructor called.\n";
}


注意:

构造函数:

1.构造函数是一个特殊函数,函数名字和类名相同,不可以有返回类型。

2.构造函数可以重载

3.程序中不能直接调用构造函数,在创建对象时系统自动调用构造函数

析构函数:

1.析构函数是一个特殊函数,名字和类名相同,但是在函数名前要加上”~”字符。析构函数不指定返回类型,也不能有参数。

2.一个类只能定义一个析构函数。

3.在下面两种情况析构函数会被调用:

(1)如果一个对象被定义在一个函数体中,则当这个函数结束时,该对象的析构函数被自动调用。即放在栈内存中的对象被回收时会自动调用析构函数。

(2)当一个对象是是使用new运算符被动态创建时,在使用delete运算符释放它时,delete将会自动调用析构函数。

5.堆对象

堆对象是指程序运行过程中根据需要随时可以建立或删除的对象。需要使用new和delete.

使用方法:

new <类名>(<初始化列表>)

new运算符返回的是指针,使用成员时需使用”->”.

使用new创建的对象不会自动回收,所以在不需要时要使用delete主动释放,否则会造成内存泄露

6.public,private,protected的使用

class 类名称
{
public:
公有成员,在关键字public后面声明,是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。
private:
私有成员和函数,在private后面声明,只允许本类中的函数访问,而类外的任何函数都不能访问。
protected:
保护型成员和函数。在protected后面声明,允许本类和派生类使用,本类外除派生类中其他地方不能使用
}


7.特殊成员函数

C++的类有几个函数是即使不定义也会自动生成的,除了文章上面讲到的默认构造函数和默认析构函数。还有复制构造函数、赋值运算符、地址运算符等。下面着重讲复制构造函数和赋值运算符,这两个函数有些时候如果不重写会造成一些难以发现的错误。

复制构造函数

复制构造函数用于将一个对象复制到新创建的对象中。类的复制构造函数原型通常如下:

Class_name(const Class_name &);
如: StringBad(const StringBad &);


下面四种声明都将调用复制构造函数:

StringBad ditto(motto);
StringBad metoo = motto;
StringBad also = StringBad(motto);
StringBad * pStringBad = new StringBad(motto);


注意:

默认复制构造函数的功能:默认的复制构造函数逐个复制非静态成员(成员复制也称为浅复制),复制的是成员的值。假如成员中有类型是指针的,只会复制该指针,而不会复制该指针指向的内容。这一点经常造成问题,需要格外注意。如果出现该问题时,解决问题的方法就是定义一个显式复制构造函数进行深复制。

赋值运算符

运算符的原型如下:

StringBad headline("A cat");
StringBad str;
str = headline;//使用赋值运算符
//初始化对象时,不一定会使用赋值运算符
StringBad metoo = str;
//上面这句代码有两种可能的执行,一种是使用复制构造函数将str复制给metoo,另一种是使用复制构造函数创建临时变量,再用赋值运算符将临时变量的值复制到新对象中。


8.自定义String类

String.h

#ifndef STRING_H_
#define STRING_H_
#include <iostream>
using namespace std;
class String{
private:
int length;
char * data;
public :
String(const char * str = NULL );//构造函数
String(const String& str);//拷贝函数
~String();//析构函数
String operator+(const String& str) const;//重载+
String&  operator=(const String& str);//重载=
String& operator+=(const String& str);//重载+=
bool operator==(const String& str) const;//重载==
int size();//返回字符串长度
friend ostream& operator<<(ostream &os, String &str);//输出
};
#endif /* STRING_H_ */


String.cpp

#include "String.h"
#include <string.h>
String::String(const char *str)//通用构造函数
{
if (!str)
{
length = 0;
data = new char[1];
*data = '\0';
}
else
{
length = strlen(str);
data = new char[length + 1];
strcpy(data, str);
}
}

String::String(const String& str){
length = str.length;
data = new char[length + 1];
strcpy(data, str.data);
}

String::~String(){
length = 0;
delete [] data;
}

String String::operator+(const String& str) const{
String newString;
newString.length = length + str.length;
newString.data = new char[newString.length + 1];
strcpy(newString.data, data);
strcat(newString.data, str.data);
return newString;
}

String& String::operator +=(const String& str){
length += str.length;
char * newStr = new char[length + 1];
strcpy(newStr, data);
delete [] data;
strcat(newStr, str.data);
data = newStr;
return *this;
}

String& String::operator =(const String & str){
length = str.length;
delete [] data;
data = new char[length + 1];
strcpy(data, str.data);
return *this;
}

bool String::operator ==(const String& str)const{
if(length != str.length){
return false;
} else {
return strcmp(data, str.data)?false:true;
}
}

ostream& operator<<(ostream &os, String &str){
os<<str.data;
return os;
}

int String::size(){
return length;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: