其他
联编
1.概念
将模块(每个文件单独的编译,就是单独的模块)或者函数合并到一起生成可执行代码的处理过程,(函数调用),按照联编所进行的阶段不同,可分为两种联编方法:静态联编和动态联编
理解
就比如说函数:
//函数实现
void print()
{
cout << "联编" << endl;
}
//函数调用
print();
两种联编区分
(1)联编就是将函数实现和函数调用联系在一起,关联在一起。程序走到print()
处,就知道要调用函数实现那一块,这就叫做联编。
(2)编译阶段不同:
a.编译阶段:编译阶段执行的叫做静态联编
b.运行阶段:运行阶段执行的叫做动态联编
(3)两种联编的区别:
a.执行阶段不同
b.静态联编在编译时关联好,动态联编在C++中针对多态,在运行时才将函数实现和函数调用关联好。但不是所有情况都需要动态联编,只是特殊的情况需要动态联编:
#include<iostream>
using namespace std;
class Cfarther
{
public:
void print()
{
cout << "farther" << endl;
}
};
class Cson :public Cfarther
{
public:
void print()
{
cout << "son" << endl;
}
};
int main()
{
Cfarther *farther ;
int b;
cin >> b;
switch (b)
{
case 1:farther = new Cfarther; break;
case 2:farther = new Cson; break;
}
farther->print();
system("pause");
return 0;
}
像这样,只有在运行farther->print();
的时候才知道调用了哪一个的对象,知道之后才将函数实现和函数调用联编,这就是动态联编。
单例模式
概念
最基本的设计模式
关键点
(1)一个类只能创建一个对象
(2)如果构造函数是私有的,类不能创建对象。
private的构造不能实例化对象(即创建对象)
单例创建步骤(4步走)
1.private不能创建对象,让构造成员函数成为私有的
class Cfarther
{
private:
Cfarther()
{
cout << "单例" << endl;
}
};
2.既然私有的构造成员不能从外部调用,那就从内部调用。
可以通过静态成员函数(因为非静态成员函数要在类外用对象调用,静态函数可以用对象调用,也可以用类名作用域调用)申请对象空间,并返回地址来调用成员。相当于在类内调用Cfarther
#include<iostream>
using namespace std;
class Cfarther
{
private:
Cfarther()
{
cout << "单例" << endl;
}
public:
static Cfarther*farther() //返回一个Cfarther*型的返回值
{
return (new Cfarther);
}
};
int main()
{
//创建对象的形式 相当于连接了通道
Cfarther *far = Cfarther::farther();
system("pause");
return 0;
}
第二步这种情况,也可以创建无数个对象。
在创建指针的时候都可以调用它,这就需要第三步
Cfarther *far = Cfarther::farther();
Cfarther *far1 = Cfarther::farther();
3.定义静态标记,记录对象个数,并控制
由于不能创建对象,所以类内的标记也是静态的
不过静态标记要在类外初始化
(1)
public:
static int flog; //定义静态标记
static Cfarther*farther() //返回一个Cfarther*型的返回值
{
return (new Cfarther);
}
};
int Cfarther::flog = 1; //静态成员,类外初始化
(2)标记判断,flog == 1进到函数里,为0不进,返回一个NULL
static Cfarther*farther() //返回一个Cfarther*型的返回值
{
//标记判断
if (1 == flog)
{
//执行完后flog就置成0,就不再执行对象创建了
flog == 0;
return (new Cfarther);
}
else
{
return NULL; //这样就只执行一次了
}
}
//主函数调用
```cpp
Cfarther *far = Cfarther::farther();
//释放空间
delete far;
Cfarther *far1 = Cfarther::farther();
理论上,far释放后,far1就能创建,但事实上还是失败了,原因是,标记是静态的标记,所以要进行改变才能让detele far后标记置成1
那么该如何改变呢?
这就用到析构函数了!
原来析构可以这么用!!长见识了!
(4)析构函数,将标记清空,以达到重复申请对象的目的
这样的意思就是,你可以重复申请,但每次只能申请一个
~Cfarther()
{
1 == flog;
}
//主函数调用
Cfarther *far = Cfarther::farther();
delete far;
Cfarther *far1 = Cfarther::farther();
delete far1;
这样就能成功创建far1了
总代码如下:
#include<iostream>
using namespace std;
class Cfarther
{
private:
Cfarther()
{
cout << "单例" << endl;
}
public:
static int flog; //定义标记
static Cfarther*farther() //返回一个Cfarther*型的返回值
{
//标记判断
if (1 == flog)
{
flog == 0; //标记置成0
return (new Cfarther); //返回Cfarther堆区空间
}
else
{
return NULL;
}
}
~Cfarther() //利用结束调用的性质
{
1 == flog;
}
};
int Cfarther::flog = 1;
int main()
{
//创建对象方式,调用farther函数
Cfarther *far = Cfarther::farther();
delete far;
Cfarther *far1 = Cfarther::farther();
delete far1;
system("pause");
return 0;
}
异常
概念
异常是人为定义的一种状况,比如说一个函数参数,当这个参数传递0的时候,我们就认为它是异常 。
异常终止函数
形式
abort()
功能
弹出异常框
测试
void text(int a)
{
if (0 == a)
abort();
}
//主函数调用
text(0);
这样就会弹出异常框
异常终止情况
关键字:try throw catch
try:测试异常
throw:遇到异常就扔出
catch:用参数接住扔出的异常,并处理
#include<iostream>
using namespace std;
void text(int a)
{
while (a < 10)
{
if (5 == a) //判断异常是5
{
throw a; //扔出a
}
a++;
}
}
int main()
{
try //检测异常
{
text(5);
}
catch (int b)
{
cout << b << endl;
}
system("pause");
return 0;
}
优势:比abort()函数灵活。
过程详解:try检测异常,若有异常,通过throw扔出,给catch用参数进行处理。也可以扔出其他类型:字符,浮点型,都行。
catch (int b)
{
cout << b << endl;
}
catch (char c)
{
cout << c << endl;
}
也可以并列,写多种处理情况,类比重载。若是整型打印b,字符型打印c。
可以在catch中再处理,让程序继续进行运行
try //检测异常
{
text(5);
}
catch (int b)
{
try
{
text(b+1); //b是5,这就继续传6了
}
catch(int c)
{
cout << c << endl;
}
cout << b << endl;
}
但如果catch
扔出的类型不知道,可以:
catch(...)
{
cout << "default" << endl;
}
注意:
catch
扔出对象的时候,要用引用或者指针,不然会拷贝备份出来。
演示:
#include<iostream>
using namespace std;
class Cfarther
{
public:
int b;
int c;
void print()
{
b = 12;
c = 5;
cout << "异常" << endl;
}
};
void text(Cfarther &farther)
{
while (farther.b < 10)
{
if (5 == farther.b ) //判断异常是5
{
throw farther; //扔出对象,就是扔出的引用
//throw &farther; 对象地址,用指针执行
}
farther.b ++;
}
}
int main()
{
Cfarther far;
try //测试
{
text(far);
}
catch (Cfarther&f)
{
f.c++; //上面是引用,所以在这里执行
}
catch (Cfarther*fa)
{
fa->c++; //上面仍的是地址,所以在这里执行
}
}
内部类
概念
其实外部类和内部类是一个相对的定义。
类A中有类B,那么类A自然就是外部类了,类B就是内部类。
内部类可以使用外部类的任何变量和方法,但是外部类必须要实例化内部类才可以使用内部类。
而且在实例化之前必须先创建一个外部类的实例。
不过也可以使用this来调用外部类和内部类的元素。
类内的类,嵌套关系
重点:相互访问;
不过无论是内部访问外部的,还是外部访问内部的都要通过对象访问
外部类访问内部类成员
下面就说明内类不能直接使用外类,会报错:未声明标识符
class Cout
{
public:
int a;
Cout()
{
a = 12;
}
public:
class Cin //内部类
{
public:
int b;
Cin()
{
b = 13;
}
void In()
{
cout << a << endl;
}
};
};
纠正:在内类还要声明一个外类的对象,通过对象调用
void In()
{
Cout out;
cout << out.a << endl;
}
调用:
想要调用In()这个函数,就要在外类内创建一个内类的对象,
然后在主函数里,通过外类对象调用在外类中创建的内类的对象,然后,再用内类对象调用函数才可以,代码如下:
Cout ou; //创建一个外类对象
ou.in.In (); //ou.in调用内类对象,in.In()调用函数
但如果
Cout ou; //创建一个外类对象
ou.a = 15;
ou.in.In (); //ou.in调用内类对象,in.In()调用函数
结果,a的值不会变!!为什么?
内类创建的外类对象是out,主函数创建的外类对象是ou。两个对象不同,各自有各自的空间,所以ou里的a值变了,但out里的还是没变的
那如何让ou传的a值改变呢??
传out的地址到内类里不就特别棒了,这里就无需创建新对象了
这里也就要用到指针来操作了
请看
在上面代码的基础上。
1)创建对象肯定行不通了,只能通过指针了,所以只能在内类中创建指针。
class Cin //内部类
{
public:
int b;
Cout*p; //定义外类指针
};
2)指针出来了,还没初始化呢!这是一个外类的指针,所以肯定指向外类的对象呀,那就装一个外类的对象ot吧!并且在函数里用指针调用。
class Cin //内部类
{
public:
int b;
Cout*p; //定义外类指针,但还是在内类里的
//通过构造函数把外部类的地址传进来,即吧this指针传进来
//这就是将外类指针初始化,装ot对象
Cin(Cout*ot) :p(ot) //初始化指针 这里的构造就有参了
{
b = 13;
}
void In()
{
//这里直接用p调用就可以了
cout << p->a << endl;
}
};
3)内类构造函数有参数了,就要在外类中声明的内类对象传参
Cin in(this); //将this指针传进来,p就是当前外部类的指针了
在外类中肯定不能这么写,因为成员不能直接赋值,所以要写在构造函数参数列表处进行传递
//内类的构造函数有参数,所以要传参
Cout() :in(this) //this指针,作为参数,传进内部类
{
a = 12;
}
总代码如下:
#include<iostream>
using namespace std;
class Cout
{
public:
int a;
//内类的构造函数有参数,所以要传参
Cout() :in(this) //this指针,作为参数,传进内部类
{
a = 12;
}
public:
class Cin //内部类
{
public:
int b;
Cout*p; //定义外类指针
//通过构造函数要把外部类的地址传进来,即吧this指针传进来
//将外类指针初始化,装ot对象,p即是外类的指针
Cin(Cout*ot) :p(ot)
{
b = 13;
}
void In()
{
cout << p->a << endl; //这里直接用p调用就可以了
}
};
public:
Cin in;
};
int main()
{
Cout ou;
ou.a = 15;
ou.in.In ();
system("pause");
return 0;
}
内部类访问外部类成员
内部类可以使用外部类的任何变量和方法
类型转换
旧式强制类型转换(int)1.2
(1.2)int
新式类型转换
某种类型固定使用某种类型的新式转换
4种运算符:
static_cast
const_cast
dynameic_cast
reinterpret_cast
无论新式还是旧式都是转换数据的,只不过新式吧数据分的更细了
static_cast
应用情况
当type和expression可以互相隐式转换的时候,这个方法执行起来才是合法的
即目标类型和当前表达式可以进行相互隐式类型转换
如:父类子类之间互相转换没问题,如果和一个不相干的类进行转换,就会报错
形式
static_cast<type>(expression)
例子:
父类和子类的两个指针进行相互转换
//将son转换为Cfarther类型的然后再赋给farther
farther = static_cast<Cfarther*>(son);
如果用强制类型转换,将子类转换成其他的类
Cother *other;
other = (Cother*)son;
这样转换肯定会成功,但子类继承父类,子类又被转换成其他类,于是乎父类和子类的成员会被合成一个数据,转换之后的类就无法保证使用的安全性。但新式类型就会报错,所以更好一点
const_cast(不常用)
应用情况
类型转换表达式const属性(去掉const),仅当typ和expression一样的时候,才合法
注意
转指针与非转指针,非指针要传地址或者引用
形式
const_cast<type>(expression)
代码如下:目的是去掉const的属性
//去掉const的属性
Cfarther *farther1 = const_cast<Cfarther*>(farther);
这是定义一个Cfarther
型新对象接改后的对象,改的时候,是将const Cfarther farther
转成Cfarther*farther
的
由旧式类型转换也可得
so = (Cson*)far; //将const去掉转成Cson*型
这里也是将const Cfarther
类型的far
强转成Cson*
型的far
dynamic_cast(不常用)
应用情况
(1)expression的类型是目标type的共有派生类
(2)expression类型是目标type类型
(3)expression类型是目标type的共有父类,父类必须是多态的情况
因为,子类的可用空间比父类要大,所以只有多态的情况,扩大父类的可用空间,才能转换成子类
(1)和(3)即type和expression是父子类关系,就是合法转换
(2)的意思是,type和expression类型都是一样的
子类和父类的转换
父类转换成子类
//父类转换成子类 父类是多态的情况
son = dynamic_cast <Cson*>(farther);
子类转换成父类
//子类转换成父类
farther = dynamic_cast <Cfarther*>(son);
注意
不相干类即使用dynamic_cast
有可能会通过,也最好不要用
因为dynamic_cast
就是针对有继承关系类的转换而存在的(前提是,父类是多态)
reinterpret_cast(不常用)
应用情况
用于危险类型转换
例如:不同的不相干的类有不同的对象,都有不同的访问方式,类型不同,访问很容易崩溃的,这种运算符就是针对像这种类型不同之间转换的情况而存在的
例如::结构体和数据类型之间的转换
注意点
(1)这种转换适用于依赖实现底层编程技术,代码不可移植,例如:不同的系统可能以不同的顺序存储多字节数据中的字节
(2)互相转换时,空间一定够用,否则不行
(3)函数指针和数据指针不能相互转换