一 类
类的访问修饰符
只有类和友元函数可以访问私有成员
保护成员比私有成员的开放性要更大一点,可以被子类访问
友元函数定义在类的外部,可以访问类的私有成员
内联函数
如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
在类定义中的定义的函数都是内联函数。
this指针
this指针是成员函数的隐含参数,友元不是类的成员函数,所以友元函数没有this指针。
类的静态成员
当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。
静态函数只要使用类名加范围解析运算符 :: 就可以访问。
静态成员函数只能访问静态数据成员。
静态成员函数不能访问类的 this 指针。
二 访问控制和继承
访问 | public | protected | private |
---|---|---|---|
同一个类 | yes | yes | yes |
派生类 | yes | yes | no |
外部的类 | yes | no | no |
派生类不会继承基类的这些方法:
析构函数,构造函数,拷贝构造函数;友元函数;重载运算符。
使用不同访问修饰符的继承特点:
公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
保护继承(protected):当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
多继承的格式:
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{
<派生类类体>
};
例如:
#include <stdio.h>
using namespace std;
class shape1{
protected:
int length, width;
public:
shape1(int val1 = 0,int val2 = 0){
length = val1;
width = val2;
}
int getLen(){
return (length + width) * 2;
}
};
class shape2{
public:
int getArea(int length, int width){
return length * width;
}
};
class drive:public shape1,public shape2{
// in most cases, we use public constructor function.
public:
drive(int val1 = 0,int val2 = 0):shape1(val1,val2){}
};
int main()
{
drive entity(10,20);
printf("the area is %d, length is %d\n",entity.getArea(10,20),entity.getLen());
return 0;
}
/* the area is 200, length is 60 */
protected可以被该类的方法和其友元函数访问,不能被该类的对象访问
三 重载运算符
写作格式:Box operator运算符(const Box&);
不可重载的运算符:
::
.*
.
?:
例子:
代码:
#include <iostream>
#include <stdio.h>
using namespace std;
class Vector{
private:
int x,y;
public:
void show(){
printf("(x, y) is (%d, %d)\n",x,y);
}
Vector(int a,int b){
x=a;
y=b;
}
Vector operator+(const Vector& other){
Vector ans(0,0);
ans.x = this->x+other.x;
ans.y = this->y+other.y;
return ans;
}
Vector operator-(const Vector& other){
Vector ans(0,0);
ans.x = this->x-other.x;
ans.y = this->y-other.y;
return ans;
}
int operator*(const Vector& other){ // * replace ·, dot product(数量积): a·b=a1b1+a2b2+……+anbn
return this->x*other.x + this->y*other.y;
}
int operator^(const Vector& other){ // ^ replace x, cross product(向量积):
return this->x*other.y - this->y*other.x;
}
};
int main()
{
Vector ve1(0,4);
Vector ve2(5,0);
Vector ans = ve1+ve2;
cout<<"for sum, ";
ans.show();
ans = ve1-ve2;
cout<<"for sub, ";
ans.show();
cout<<"for dot product, result is "<<(ve1*ve2)<<endl;
cout<<"for cross product, result is "<<(ve1^ve2)<<endl;
return 0;
}
执行:
for sum, (x, y) is (5, 4)
for sub, (x, y) is (-5, 4)
for dot product, result is 0
for cross product, result is -20
四 多态
关键词:虚函数。动态链接,后期绑定。
#include <stdio.h>
using namespace std;
class father{
public:
virtual void show(){ // virtual 使得编译器看指针的内容,而不是指针的类型。
puts("I'm father");
}
};
class son1:public father{
public:
void show(){
puts("I'm son1");
}
};
class son2:public father{
public:
void show(){
puts("I'm son2");
}
};
int main()
{
father *p = NULL;
son1 entity1;
son2 entity2;
p = &entity1;
p->show();
p = &entity2;
p->show();
return 0;
}
执行:
I'm son1
I'm son2
我们可以使用纯虚函数,不给出父类的show的函数体。
即是virtual void show() = 0;
五 抽象类
如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。
抽象类不能被用于实例化对象,它只能作为接口使用。
六 类的其他补充内容
类的私有内部表示被称为是封装的(encapsulated) 而类的公有部分被称为类接口(class interface)
除了静态(static)数据成员外,数据成员不能在类体中被显式地初始化。
如果没有指定访问修饰符,在缺省的情况下定为private。
友元机制允许一个类授权其他的函数访问它的非公有成员。不受public private 和protected的影响。例如:
class Screen {
friend istream&
operator>>( istream&, Screen& );
friend ostream&
operator<< ( ostream&, const Screen& );
public:
// ... Screen 类的其他部分
};
在使一个类成为友元时,友元类的所有成员函数都被给予访问授权友谊的类的非公有成员的权力.
一个类不能有自身类型的数据成员,可以用指向自身类型的指针或引用作为数据成员.
一个对象可以被同一类类型的另一个对象初始化或赋值.
Screen *ptr = new Screen;//Screen是一个类
...
delete ptr; //等价于ptr=null;
s2->height() //可以写为 (*s2).height
inline: 在类定义中定义的内联函数.
如果成员函数被定义在类体之外那么就需要类名限定修饰。–>” 类名::” , 但是成员函数必须先在其类体内被声明而且类体必须在成员函数被定义之前先出现,没有在类体中定义的内联成员函数必须被放在类定义出现的头文件中。
new表达式返回对象的地址。
string *ps=new string;
delete ps; //释放ps指向的string对象还给空闲存储区。
int *p=new int[10];
delete [] p;
七 函数模版
函数模板提供一个种用来自动生成各种类型函数实例的算法
宏替换的缺陷:
#define myMin(a,b) ((a)<(b)?(a):(b));
int main() {
int a=3,b=5;
a= myMin(a++,b++);
cout<<a<<endl;
return 0;
}
/*
4
*/
函数模板的优势:
template <class Type>
Type myMin( Type a, Type b ) {
return a < b ? a : b;
}
int main() {
int a=3,b=5;
cout<<myMin(a++,b++)<<endl;
cout<<myMin( 10, 20 )<<endl;
cout<<myMin( 10.4, 20.9 )<<endl;
cout<<myMin(10LL,20LL)<<endl;
return 0;
}
/*
3
10
10.4
10
*/
模板类型参数:template <class Type>
模板非类型参数: template <class Type, int size>
//其中的int size就是非类型参数
在函数模板参数表中关键字typename 和class 的意义相同可以互换使用
类型和值的替换过程被称为模板实例化(template instantiation)
函数模板也可以被声明为inline 或extern 应该把指示符放在模板参数表后面而不是在关键字template 前面
template <class type>
type sum(type a[],int len){
type s=0;
for(int i=0;i<len;i++){
s=s+a[i];
}
return s;
}
int main() {
int a[5]={1,2,3,4,5};
double f[5]={1.1,2.1,3.1,4.1,5.1};
cout<<sum(a,5)<<endl;
cout<<sum(f,5)<<endl;
return 0;
}
/*
15
15.5
*/
template <typename Type, int len>
Type min( Type (&r_array)[len] ) // (&r_array)[len] --- what ?
{
Type min_val = r_array[0];
for ( int i = 1; i < len; ++i )
if ( r_array[i] < min_val )
min_val = r_array[i];
return min_val;
}
int ia[] = { 10, 7, 14, 3, 25 };
double da[6] = { 10.2, 7.1, 14.5, 3.2, 25.0, 16.8 };
int main()
{
cout<<min(ia)<<endl;
cout<<min(da)<<endl;
return 0;
}
显式模板实参
改变模板实参推演机制,模板实参被显式指定在逗号分隔的列表中并使用显式指定,用尖括号括起来。紧跟在函数模板实例的名字后面
我们希望定义一个名为sum()的函数模板以便从该模板实例化的函数可以返回某一种类型的值该类型足够大可以装下两种以任何顺序传递来的任何类型
的两个值的和。
template <class T, class U>
??? sum( T, U );
char ch; unsigned int ui;
// T 和U 都不用作返回类型
sum( ch, ui ); // ok: U sum( T, U );
sum( ui, ch ); // ok: T sum( T, U );
一种方案是通过引入第三个模板参数来指明函数模板的返回类型
// T1 不出现在函数模板参数表中
template <class T1, class T2, class T3>
T1 sum( T2, T3 );
C++ 模板编译模式:
C++ 支持两种模板编译模式包含模式(Inclusion Model) 和分离模式(Separation Model)
包含模式:模板定义放在头文件中
真正的实例化动作发生在何时何地要取决于具体的编译器实现.
min.h:
#ifndef MIN_H_INCLUDED
#define MIN_H_INCLUDED
template <class Type>
Type mymin(Type t1,Type t2){
return t1<t2?t1:t2;
}
#endif // MIN_H_INCLUDED
main.cpp:
#include <iostream>
#include "min.h"
using namespace std;
int main()
{
int a0=12,b0=34;
double a1=12.3,b1=12.67;
cout<<mymin(a0,b0)<<endl;
cout<<mymin(a1,b1)<<endl;
return 0;
}
分离模式:
分离编译模式允许我们分离函数模板的声明和定义,函数模板的声明被放在头文件中。
关键字export 告诉编译器在生成被其他文件使用的函数模板实例时可能需要这个模板定义。
min.h:
#ifndef MIN_H_INCLUDED
#define MIN_H_INCLUDED
template <class Type>
Type mymin(Type t1,Type t2);
#endif // MIN_H_INCLUDED
define.cpp:
export template <class Type>
Type mymin(Type t1,Type t2){
return t1<t2?t1:t2;
}
main.cpp:
#include <iostream>
#include "min.h"
#include "define.cpp"
using namespace std;
int main()
{
int a0=12,b0=34;
double a1=12.3,b1=12.67;
cout<<mymin(a0,b0)<<endl;
cout<<mymin(a1,b1)<<endl;
cout<<mymin<int>(a1,b1)<<endl; // 显式实例化
return 0;
}
支持分离模式需要更复杂的程序设计环境, 所以它们不能在所有C++编译器实现中提供.
跨文本调用函数:
min.h:
#ifndef MIN_H_INCLUDED
#define MIN_H_INCLUDED
template <class type>
type mymin(type t1,type t2){
return t1<t2?t1:t2;
}
#endif // MIN_H_INCLUDED
min.cpp:
#include <cstdio>
void another() {
puts("this is a statement from another file.");
}
main.cpp:
#include <iostream>
#include "min.h"
#include "another.cpp"
using namespace std;
int main()
{
char s1='a',s2='d';
cout<<mymin(s1,s2)<<endl;
cout<<mymin<int>(s1,s2)<<endl;
another();
return 0;
}
/*
a
97
this is a statement from another file.
*/
模板函数的重载:
#include <iostream>
using namespace std;
template <class type>
class Array{
public:
type *element;
int length;
Array(int len){
element=new type[len];
length=len;
}
~Array(){ delete element; }
void show(){
for(int i=0;i<length;i++){
cout<<element[i]<<" ";
}
cout<<endl;
}
};
template <class type>
type mymin(type t1,type t2){
return t1<t2?t1:t2;
}
template <class type>
type mymin(Array<type> ar){
type minm=1<<30;
for(int i=0;i<ar.length;i++){
minm=minm<ar.element[i]?minm:ar.element[i];
}
return minm;
}
int main()
{
Array<int> intA(10);
Array<double> douA(10);
for(int i=1;i<11;i++){
intA.element[i-1]=i;
douA.element[i-1]=i*1.1;
}
intA.show();
douA.show();
cout<<mymin(12,34)<<endl;
cout<<mymin(intA)<<endl;
cout<<mymin(douA)<<endl;
return 0;
}
/*
1 2 3 4 5 6 7 8 9 10
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 11
12
1
1.1
*/
调用可以被解析为函数模板的实例或者被解析为普通函数,到底调用哪个函数取决于这些函数中的哪一个与函数实参类型匹配得最好。