- 类
- 类定义
- 构造函数与析构函数
- 构造函数
- 析构函数
- this指针
- 使用const意义
- 运算符重载
- 友元函数
- 技巧1
- 类的自动转换和强制类型转换
- 类的自动转换
- 类的反转换
- 类和动态内存分配
- 特殊成员函数
- 默认构造函数
- 复制构造函数
面向对象编程(OOP)是一种特殊的、设计程序的概念方法,c++通过一些特征改进了c语言,使得这些方法更容易(抽象、封装和书籍隐藏、多态、继承、代码的可重用性)。一般定义类名时会将第一个首字母大写
class Merge {//创建类
public://共有成员,对象可以直接访问
int input(int num);//类方法源函数
private://私有成员,类方法可以访问
int num;//私有成员变量。
};
int Merge::input(){return 0;}//类方法定义
int main(){
Merge mer;//定义对象
mer::input();//::调用类的公共方法,每个对象都有一个属于自己的存储空间,但是同一个类的所有对象都共享同一组类方法,
return 0;
}
构造函数与析构函数 构造函数一个类就相当于一个命名空间(类存在友函数),调用类中的函数,使用也是与调用命名空间相识。
构造函数与类名同名,程序声明对象时,将自动调用构造函数,通常会用来对于对象进行初始化,因为类不同于构造函数可以直接初始化,在类中存在五法直接访问的隐藏数据,需要使用内部的函数对对象进行初始化。
class Stock{
Stock(int n);//构造函数源函数
}
Stock::Stock(int n){}//构造函数定义
int main(){
Stock st1(666);//调用方法1
Stock st2=Stock(666);//调用方法2
Stock * pst= new Stock(666);//将构造函数与new一起使用。
return 0;
}
析构函数构造函数有两种调用方法,第一种是隐式的调用,第二种是显式的调用。
定义构造函数时添加默认值Stock(int n =1);或进行函重载Stock()
this指针析构函数原型必须是~Stock()不能带有任何参数,其在对象结束时将会被调用,定义Stock::~Stock(),通常被用于对象中,有使用new时调用delete对内存进行释放。
当需要对两个对象的成员变量做比较时,可以使用,用变量接收一个带有返回值的成员函数,另外可以使用this的方法返回对象;
cons Stock&Stock::topval(const Stock &s)const{
if(s.n>n)
return s;
else
return *this;
}
使用const意义
运算符重载几个要注意的点
- 对于加在成员函数后的const,表示的是该函数不可以修改对象的成员变量。
加在非成员函数或静态成员后将会产生报错。
例外是对于mutable修饰的成,员加了const的成员函数,可以被非cosnt对象和const对象调用,但是不加const的成员函数只能被非const对象调用。
char getData()const{return this->letter;}- 每个成员函数都有一个this指针。this指针指向调用对象。要返回的不是this,因为this是对象的地址,而不是对象本身,所以返回*this。
在函数后加const限定符将this限定为const,这样不能使用this来修改对象的值。
operator (运算符) ()可以对运算符进行重载。
下面样例:对传入的两个时间进行想加。
class Time {//创建时间类
public:
Time();
Time(int h,int m);
Time sum(Time & s);//普通方法形式
Time operator + (Time& s);//运算符重载
void show();
private:
int hours;//小时
int minutes;//分钟
};
Time Time::sum(Time& s) {//定义
Time sum;
sum.minutes = s.minutes + this->minutes;
sum.hours = s.hours + this->hours + sum.minutes/60;
sum.minutes = sum.minutes % 60;
return sum;
}
Time Time::operator+(Time& s) {//与sum定义完全相同
Time sum;
sum.minutes = s.minutes + this->minutes;
sum.hours = s.hours + this->hours + sum.minutes / 60;
sum.minutes = sum.minutes % 60;
return sum;
}
Time::Time() {
this->hours = 0;
this->minutes = 0;
}
Time::Time(int h_, int m_) {
this->hours = h_;
this->minutes = m_;
}
void Time::show() {
std::cout << this->hours<<":" << this->minutes << std::endl;
}
int main(){
Time a1{ 1,1 };
Time a2{ 1,2 };
a1.sum(a2).show();
a1.operator+(a2).show();
(a1+a2).show();
//输出
//3:10
//3:10
//3:10
return 0;}
友元函数使用operator重载函数后可以对对象直接使用加号运算符,就能够达到调用sum()函数的效果。
创建友元函数需要在类中声明,并在原型申明前面加上关键字friend。虽然其在内部声明但是其并不可以使用成员运算符来调用。它有成员函数相同的访问权限。
friend Time operator * (int n,const Time &t);//原型
Time operator * (int n,const Time &t){}//定义
Time temp = n*a1
temp = operator*(n,a1);
友元函数在定义时不需要加friend也不能使用Time::限定符。
对于<<运算符重载,添加友元函数使得std::cout能够直接的输出Time对象。
class Time {//创建时间类
public:
Time();
Time(int h,int m);
void show();
friend std::ostream & operator <<(std::ostream& os, const Time& t);
private:
int hours;//小时
int minutes;//分钟
};
Time::Time() {
this->hours = 0;
this->minutes = 0;
}
Time::Time(int h_, int m_) {
this->hours = h_;
this->minutes = m_;
}
void Time::show() {
std::cout << this->hours<<":" << this->minutes << std::endl;
}
std::ostream & operator <<(std::ostream& os, const Time & t) {
os << t.hours << "hours," << t.minutes << "minutes" << std::endl;
return os;
}
int main(){
Time a1{ 1,1 };
Time a2{ 1,2 };
std::cout << (a1 + a2)<<777<
注意在对<<重载时返回值是引用,os对象是引用进函数的,同样也可以引用出去。同时如果不这样做,那么后面<<777将无法正确的输出。
输出2是输出1的调用展示。运算符的重载调用的是
技巧1
使用全局的算数符重载,弥补类中算数符重载遇到的问题(n*对象无法解析),
class Stock {
public:
Stock();
Stock(int n);
Stock operator*(double n);
void show();
private:
int a;
};
Stock::Stock() {
this->a = 0;
}
Stock::Stock(int n) {
this->a = n;
}
Stock Stock::operator*(double n) {
this->a *= n;
return *this;
}
void Stock::show() {
std::cout << this->a;
}
Stock operator*(double n, Stock& o) { //全局函数使用去全局函数重载*,将其重载给Stock的成员函数
return o * n;
}
int main(){
Stock a1{ 1 };
a1 = a1 * 6;
a1 = 6.0 * a1;
a1.show();
return 0;
}
//输出
//36
使用全局的运算符重载使得,将Stock成员函数不能调用的a1=6.0*a1,转化为成员函数能够调用的格式;
类的自动转换和强制类型转换
在C++98中只可以在构造函数前,加explicit使其变为强制类型。
才C++11中可以在转换函数钱,加explicit使其变为强制类型。
应谨慎的使用隐式转换类型,通常,最好选择使用在被显示调用时才会执行的函数。
类的自动转换
与定义的构造函数相关
将对象Stock初始化为doublel类型时。
将double值赋值给Stock对象时
将double值传递给接收
class Stock{
public:
Stock(double n);
};
类的反转换
与定义的转换函数有关
将类转换为特定的类型。
转换函数的格式:
peratortypename();
转换函数必须是类方法,不能指定返回类型,不能有参数。
class Stock{
public:
Stock();
Stock(int n);
operator double();
private:
int a;
};
Stock::Stock(){this->a=0;}
Stock::Stock(int n){this->a=n;}
Stock::operator double(){
return int(this->a);
}
int main(){
Stock a1(1);
std::cout<
在main函数中的输出是允许的,因为Stock类只有一个类型转换函数operator double()。
类和动态内存分配
特殊成员函数
C++98自动提供的成员函数
- 默认构造函数,如果没有定义构造函数;
- 默认解析函数,如果没有定义解析函数;
- 复制构造函数,如果没有定义;
- 赋值运算符,如果没有定义;
- 地址运算符,如果没有定义;
C++11额外提供了
- 移动构造函数;
- 移动赋值函数;
//创建拟string
class StringBad {
private:
char* str;
int len;
static int num_strings;
public:
StringBad();
StringBad(const char*s);
~StringBad();
friend std::ostream& operator <<(std::ostream& os, const StringBad& st);
};
//创建拟string
int StringBad::num_strings = 0;
StringBad::StringBad() {
this->len = 4;
this->str = new char[len + 1];
this->num_strings++;
}
StringBad::StringBad(const char * s) {
this->len = std::strlen(s);
this->str = new char[len + 1];
std::strcpy(this->str, s);
this->num_strings++;
}
StringBad::~StringBad() {
--this->num_strings;
delete[] str;
}
std::ostream& operator << (std::ostream& os, const StringBad& st) {
os << st.str;
return os;
}
//全局函数
int md1(const StringBad s){}
int md2(const StringBad & s){}
int main(){
StringBad a1;
md2(a1);
std::cout<
- 这些都是因为,复制函数和赋值运算符使用默认值引起的。
- 要注意对于static进行初始化(int StringBad::num_strings = 0;),否则将会弹出引用外部符号的报错。使用static限制符,变量会变成所有类对象共享的。
默认构造函数
ClassName(){};
Ikun(){};
Ikun(){Ikun_ct=0;};
Ikun(int n=0){Ikun_ct=n;};
复制构造函数
ClassNaem(const ClassName &)
Ikun(const Ikun &);
//下面四种情况将会调用复制构造函数
Ikun ditto(motto);
Ikun metto = motto;
Ikun also = Ikun(motto);
Ikun * pIkun = new Ikun(motto);



