类——前面(综合)(六)

1.C++最重要的特性之一——类,该如何定义呢?

类,作为面向对象编程的基础,定义由struct或class开始。
如:

class A
{
};

注意:最后的花括号要加分号

2.class 和struct的区别

class后成员,若不加访问说明符,默认private(继承也是如此),struct则为public。

class A{int a;//默认private
};
struct B{int b;//默认public
};

3.构造与析构

类都有一个构造与析构函数。其返回值为类类型。这两个函数必须为public的。如果不说明,编译器将隐式生成默认版本。
显式要求如下:

class A{public://必须!A()=default;//类名+括号+=default~A()=default;//多加一个
};

4.类与作用域

有以下代码:

class A{public:ostream& print(ostream&os,string as);
};
class B{public:ostream& print(ostream&st,string bs);
};

虽然都可简写为:

ostream& print(ostream&,string);

但是,每个类实际上是一个作用域。
所以,两者是有区别的。如下代码:

int main()
{A a;B b;a.print(cout,"Hello!");//是A::printb.print(cout,"Hello!");//是B::print
}

4.新的访问控制符

除了public和private以外,还有一种访问控制符,就是protected。protected介于public和private中间。public:派生类,类内,类外,友元均可访问。private:类内,友元可访问,其余不可。protected:类内,派生类(无论继承的是什么,只要继承该类),友元可访问。例子:

class A{private:int a;protected:int b;public:int c;
};
class B:public A{public:int d;
};//B成员可访问b,c
class C:private A{//private可写可不写(默认)public:int e;
};//C成员可访问a,b,c

5.this 指针

this指针是类内的一个隐式(也可使用)指针。所以,你可以返回它。如:

#include
using namespace std;
class A{public:A& where(char);char b;
};
inline A::A& where(char a)
{//因为在类的作用域内,所以无需用 .来访问。b=a;return *this;//返回this
}; 

6.静态成员

静态成员不属于任何其他成员。此外,静态成员还可以是不完全类型。如:

class A{public://...private:static A type;A *you;A error;//错误:数据成员必须为完全类型
};

7.构造函数(二)

构造函数除了有默认形式

class A{public:A()=default;
};

以外,还有其它形式,如分别用给定初始值初始化。如:

class A{public:float a;int b;A(float c): a(c),b(c){}//为空
};

也有其它的,如:

class A{public:int a;A() {}
};

8.union——节省空间的类(一)

union是一种特殊的类。它的成员中每个时刻都只有一个成员是已知的。若一个成员变为已知的,则其它成员变为未知状态。union 默认形式同struct。如:

union A{
//默认公有int a;string b;float c;
};

9.友元

友元可以访问类的任何成员。定义友元以friend 关键字开始。如:

class A{friend void who(const string&);private:string a[5];//who()可访问map<string,string>b;//同上public://...};

如果一整个类作为友元,则可定义为友元类。如:

class A{
public:void f();void g();
};
class B{friend class A;//...
};

10.较大的类的拆分——派生类

太大的类,不仅十分繁杂,且有点面向过程的感觉。因此,建议大家拆分成基类(将派生类组合起来)和派生类(实现)或全局函数。
下面是一个例子:

class count_money{protected:map<string,float> name_price;float all(const map<string,float>);bool enough(const float&);private:int time;int isbn(){ return time++; }//购买编号double minus_price(float&price1){ return 0.8*price1; } //打折float much_minus(float&price){ return price-200; }public:double total;float counts();void present();counts()=default;~counts()=default;
};

这个类较复杂,可以拆分:

class counts_money{protected:map<string,float> name_price;float all(const map<string,float>);private:int time;public:int total;int isbn(){ return time++; }
}; 
class minus:public counts_money{private:bool enough(const float&);public:double minus_price(float&price1){ return 0.8*price1; } //打折float much_minus(float&price){ return price-200; }float counts();
};
void present(); 

每个类都较简洁。

11. 作用域与隐藏名字

作用域需用::来访问,或用using指示以省略::来访问。
类也有一个作用域,所以,尽管两个类的成员完全相同,其成员认识有差别的。如:

class same{public:default_random_engine same;void get_number();
};
class same_too{public:default_random_engine same;void get_number();
};

我们看到,两边的成员是完全一样的,但是,他们处在两个不同的作用域中,所以是有差别的。

隐藏

由于是两个作用域,所以互相之间隐藏了其它作用域的名字。

12.局部作用域与静态成员

一般来说,局部作用域一般指函数的作用域,离开作用域之后所有非静态成员都将会执行析构函数。但是,静态成员除外。
当我们需要保存上一次函数的运行数据是,局部静态成员就非常有用了。
如以下代码:

class example{public:int count_calls_times(){static int times;return timrs++;//因为是局部静态成员,所以times并不会被析构}
};

局部作用域不可用“::”访问

由于局部作用域的成员会被销毁(静态成员除外),所以我们不能用::来访问。

13.初识嵌套类及嵌套类作用域

嵌套类是指在类内部,成员函数之外定义的类。但是,嵌套类也是一个独立的类,因此,它也有可见性。外部类可以用访问说明符来操纵嵌套类的可见性。嵌套类在外部类中可以访问,出了外部类便需要::来访问。如:

class A{public:class B{};
};

B是外界可以用::来访问的。

class A{public:class B{};B* pointer;//是合法的
}; 

但是如果为private或protected,类的用户便不可访问了。

同第1章所讲,外部类的成员也会被隐藏

14.进入类的作用域

当我们在类内声明了一个成员函数,要在类外定义时,就需要进入类的作用域。进入作用域以::开始。

class A{public:ostream& print(ostream&sto,const string&ost);
};
ostream& A::print(ostream&sto,const string&ost)//有A::,我们进入A的作用域了
{sto<<ost;return sto;
}

只要加了类名::,就进入了类的作用域

覆盖警告:

你有可能会在类内定义一个在其它作用域中已经出现过的变量。这是,你便要小心二义性错误了。如如下代码是错误的:

class error{public:int a;
};
class error_too{public:int a;
};
int main()
{error object;object.a;//哪个a?//...return 0;
}

上述代码是错误的,因为它没有说明是哪个a。应如何改呢?答案如下:

int main()
{error object;object.error::a;//或error_too::a;//...return 0;
}

小心哦!!!

无需担心与全局作用域混淆

因为全局作用域中的变量不需要用.来访问,因此无需担心会混淆。

14.构造函数(三)

构造函数在用一个成员来初始化另一个成员的时候,一定要注意顺序。初始化顺序要跟成员的声明顺序一致。如以下代码是未定义的。

class A{
private:int i;int j;
public:A(int k):j(k),i(j){ }
};

初识化顺序与成员声明顺序相同。因此这段代码的意思是:用未初始化的j来初始化i!
我们应该这样改:

A(int k):i(k),j(i){ }

14.1默认构造函数

当一个类有多个构造函数时,默认构造函数时不可少的。请注意!

class A{
public:A()=default;//如有其它构造函数,此函数必须!//...
};  

15.重载运算符

重载运算符不建议经常使用,但是必要时还是可以的。

重载运算符不可以创造运算符

15.1.重载运算符与this指针

在类的内部,有一个名为this的隐式指针。它指向获取成员的对象。因此,我们在类内重载运算符的时候,要特别注意this指针是一个隐式指针。

class A{
public:operator+(A&);//隐式为(A&,*this)//...
};

15.2.重载运算符与成员函数

有些运算符必须为成员函数。如:[]等。还有几个运算符不可重载. .* ?:

16.编译器处理类在int main()中的成员调用与获取

16.1.this指针

这一切与this指针还是脱不了干系。实际上,编译器在处理成员函数的时候,会自动的加上一个实参:const *this。因此,有如下代码:

class A{
public:ostream& print(const string=" ");
};
int main()
{A a;a.print();
};

编译器将翻译为:

ostream& print(const string=" ");
//伪代码
print(&a);

此时*this=&a;

17.编写辅助的类

17.1.概述

有时,由于有继承关系,我们常常会要将基类和派生类放在同一个集合中(容器),但是,由于基类不能转化为派生类类型,所以我们不能再容器内放入派生类类型的对象。如果我们放入基类对象,虽然也可以继续放入派生类对象,但是,他们再也不是派生类对象了(详见18章)。
因此,我们常常会编写一些辅助的类来处理此情况。
参考以下代码(使用第10章代码):

class counts_money{protected:map<string,float> name_price;float all(const map<string,float>);private:int time;public:int total;int isbn(){ return time++; }
}; 
class minus:public counts_money{private:bool enough(const float&);public:double minus_price(float&price1){ return 0.8*price1; } //打折float much_minus(float&price){ return price-200; }float counts();
};
void present(); 

现在,我们添加一个用户类:

class account:public counts_money{private:double all_price=0.0;void add(const string&,const double&);void minus(const string&,const double&);public:map<string,double> buys;void control();
};        

那么问题来了:如果现在定义一个储存购买物品的容器,该放什么类型的对象呢?
根据17.1,我们不应该放任何对象,那么,我们最好写一个类。

关于更加深的关于类的知识,详见第七章:C++类(七)《一谈类的更深与番外》(预计2020四月初,三月的写成)

如有错误,请在评论中提出。
感谢!!!


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部