C
(抽象、封装(安全性)、继承、多态)
封装:各个对象独立,且对象隐私部分,对外隐蔽
如果未写明限制幅(public: private: protected: )则默认为私有
声明函数可以只声明参数类型,定义函数,形参类型、形参名称全写
函数重载,同名函数(根据形参不同,来调用,)max(int a) max(float a)
代码语言:javascript复制//define 替换问题
#define s(i) i*i
//那么,s(a b)= a b*a b,
#define s(i) (i)*(i)
//.........正确,define 是个单纯的文本转换,写成函数,就不会出现这样的问题
//C 基础——C string类型终端输入字符串中含有空格解决方法
string str; //str.size();for(<str.size())
getline(cin,str);
cout<<str;
//用const 定义常变量
const float PI = 3.14 ;
//模板函数————对重载函数的优化(所有重载函数形参的数量上必须一致才可以用)
#include <iostream>
template <typename T> //必要声明
T max (T a, T b, T c) // 建立函数模板时,只需要
{ if (b>a) a=b; // 将函数类型、参数类型 int 换成 T 就行。
if (c>a) a=c; // 即用虚拟的类型名T代替实际的类型
return a;
}
int main( )
{ int i = 8, j = 3, k = 4, h;
long m = 1, n = 2, p = 3, q;
h = max ( i, j, k);
q = max ( m, n, p);
cout <<"int_h = "<<h<<endl;
cout <<"long_q = "<<q<<endl;
return 0;
}
//默认参数(php中常见)
//要求:非默认形参在左,默认参数在右,防止起争议
void Div(double a,double b=1.0)
{
}
//变量的引用
#include <iostream.h>
int main( )
{ int a = 10; int &b = a;
a = a * a; // a的值发生变化,b的值一定发生变化 ,且b能再次发生引用,不能再引用其它量
cout << "a = "<<a<<endl;
cout << "b = "<<b<<endl;
return 0;
}
//swap()在标准命名空间std里
#include <iostream>//这里为了不调用原有的swap,特意没写std,这就是sawp的内部,用了变量引用
void swap ( int &a, int &b )
{ int temp;
temp = a;
a = b;
b = temp;
}
int main( )
{ int i=3, j=5;
swap ( i, j );
cout<<i<<“,”<<j<<endl;
return 0;
}
//内联函数(主函数中高频率出现的代码段。表面是一个函数,其实内敛于主函数,仍是主函数内的一部分)
//内联部分不能出现循环 switch 递归 静态变量
#include <iostream>
using namespace std;
inline int power(int x ) // 定义一个内联函数
{ return x*x; }
int main( )
{ cout << power ( 2 ) << endl; // 编译系统展开为{x=2;x*x;}
cout << power ( 1 2 ) << endl;
// 编译系统展开为{x=1 2;x*x;}
return 0; }
//字符串变量#include<string>
#include <iostream>
#include<string>
using namespace std;
string s2;
string s1;
int main(){
cin>>s1>>s2;
cout<<s1 s2<<endl;//可以相加,可以进行字典序比较,s1>s2 s1<s2
}
//作用域运算符
//::a 就可以在局部调用全局a,而不会和局部的a冲突
//动态分配内存 new 销毁 delete
代码语言:javascript复制//遍历string变量
string a;
cin>>a;
for(int i=0;i<a.size();i ){
//a[i]...
}
//倒序遍历
#include<algorithm>
int a[]={1,2,3,4,5};
reverse(a,a 5);
for(int i=0;i<5;i )
cout<<a[i]<<endl;
//5 4 3 2 1
//cout补位
#include<iomainip>
int a = 1;
cout<<setw(3)<<setfill('0')<<a;
//001
在类里面,属性往往是私人的,对外禁止访问,方法是作为接口,向外公开的
代码语言:javascript复制//每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数
//构造函数(可重载)
class Time
{
private:
int hour, minute;
public:
Time()//不带参数的构造函数
{
hour = 0;
minute = 0;
}
Time(int h,int m) //这是带参数的构造函数,与上一个互为重载构造函数
{
hour=h;
minute=m;
cout<<h<<' '<<m<<endl;
}
};
int main()
{
Time BJ(1,2);//定义并初始化BJ对象
Time TJ;//以前定义方式,其实是调用了一个系统默认的构造函数,代码区空的
//对象数组
Time t[3]=
{
Time(1,2),
Time(2,3),
Time(3,4)
};
}
//构造函数和析构函数的调用顺序
//构造:按照分配空间顺序
//析构:按照最后全局、其次静态(static)、再其次以main函数为作用域的对象、再其次当前块为作用域的对象(补充:构造函数,静态的就算在块中,如在for循环中,每一次for循环都会使非静态构造函数执行一次构造和析构,静态构造函数只会被执行一次,main结束以后析构,神奇)
//块级别局部静态对象,作用域于局部的块,但生命周期和全局变量一样(对上一行的解释)
代码语言:javascript复制class Time{
private: int hour,int minute;
public:
Time()//空参数,称为一个默认构造函数
{
}
//析构函数
~Time(){
//...可不写,这就和系统默的析构函数一模一样了
}
};
代码语言:javascript复制//对象指针,目前能不用就不用,会给别人的理解带来困难,this指针也是如此
Time *p= new Time[3];//得到三个Time对象空间,没有new之前指针是不会调用构造函数的
delete []p;//释放动态空间,
//注意:给字符串分配动态空间的时候,要多分配一个空间给'/0'new [strlen(str) 1]
//这样,不同的编译器可能会自动分配'/0'的空间,有的可能不会,不会的就会运行出错
代码语言:javascript复制//常对象
const Time t1(1,2,3);
//用于对象数据都不可变的情况
//1.在t1的生命周期里,数据成员值不可改
//2.成员函数只能调用const成员函数,以及构造函数、析构函数
//3.可以"少量"使用mutable 使常函数强行修改常对象成员数据 如 mutable int a;
#include<iostream>
using namespace std;
class R{
public:
R(int r1, int r2) : r1(r1), r2(r2) { }//常数据成员初始化:构造函数的参数初始化表
void print( )
{
cout << r1 << ":" << r2 << endl;
}
void print( ) const//常成员函数,这里,算是一种重载
{
cout << r1 << ":" << r2 << endl;
}
private:
int r1, r2;
};
int main()
{
R a(5,4);
a.print( );
const R b(20,52); //定义常对象
b.print( );
return 0;
}
//常数据成员
//不可变,不同对象之间不共享
//只能通过构造函数参数初始化表初始化(例子如下)
//可以在非const对象内创建
//静态常数据成员是在所有函数之外初始化
//常/非常成员函数都可以调用常数据成员
#include<iostream>
using namespace std;
class Time // 类定义
{
public:
static const int hour;
const int second;
Time(int s): second(s) {} //常数据成员初始化:构造函数的参数初始化表
void out()
{
cout<<hour<<endl;
cout<<second<<endl;
}
};
const int Time::hour = 50;//类外初始化 (类名)::(作用域符号)
int main()
{
Time demo2(10);
Time demo1(11);
demo1.out();
demo2.out();
}
//常成员函数
//可以引用任意的数据成员
//可以在非const对象内创建,如果存在const导致的重构的话,const的不会被非const对象优先调用
//主要是在常对象中使用,常对象能使用的函数只有它了
//常指针
//为了固定一个对象的地址,该指针不再指向其他地方
const Time*p=&t1;//常对象
p->display();
p->change();//错误,不能通过常指针实现对常对象修改值的操作
const Time*p=&t1;
p->display();
p->change();//可以,能通过常指针实现修改值的操作
//利用常指针作为函数的形参
//为了不想改变指针值
//指向常对象的指针必须是常指针
//常引用
//动态分配空间
//new delete要成对存在,养成习惯,避免占用内存太多
// p = new type;
//delete p;
//对象的赋值 A = B
//即A的成员数据 = B的成员数据 成员数据申请的不能是动态空间
//对象的拷贝 拷贝构造函数
// 类的对象拷贝的简单例子
class C{
private:
int a;
public:
// 构造函数
C(int b): a(b){
cout << "这是构造函数....n";
}
// 拷贝构造函数:一种特殊的构造的函数,函数的名称必须与类名一样,它必须的一个参数是本类类型的一个引用变量
C(const C &C_rename){ // 这里没有自定义拷贝构造函数的话,必须使用系统默认的拷贝构造函数
a = C_rename.a;
cout << "这个是拷贝构造函数......n";
}
// 一般的成员函数
void show(){
cout << "a = " << a << endl;
}
};
C c1_obj(666);
C c2_obj = c1_obj; // 等价于C c2_obj(c1_obj);
c2_obj.show();
//静态数据成员
//在未定义对象前就分配空间,且,所有对象共用一个值
//实现了数据共享,同类所有对象范围内的全局变量,比全局变量,等效更安全
//类外初始化
class T
{
static int d;
public:
static void show()
{
cout<<d<<endl;//静态成员函数 原则是只调用静态变量
//非要调用非静态变量,你得指明他是哪个对象的,a.d之类的
}
};
int T::d=1;
int main()
{
T demo;
cout<<demo.d;//可以对象引用
cout<<T::d;//也可以直接类引用
}
//全局变量周期
//this指针指向类的对象本身,而static静态成员函数不依赖对象调用,它不与任何的对象相联系,故不会使用this指针。
//友元(强袭破坏封装的一种行为,不建议用)
//特殊访问私有数据方式
//友元函数的作用就是提供直接访问对象的私有成员的接口。通过友元,一个不同函数或另一个类中的成员函数可以访问类中的私有成员和保护成员。
//使用情况:
//1)必须在类的说明中说明友元函数,说明时以关键字friend开头,后跟友元函数的函数原型,友元函数的说明可以出现在类的任何地方,包括在private和public部分;
//2)注意友元函数不是类的成员函数,所以友元函数的实现和普通函数一样,在实现时不用"::"指示属于哪个类,只有成员函数才使用"::"作用域符号;
//3)友元函数不能直接访问类的成员,只能访问对象成员;
//4)友元函数可以访问对象的私有成员,但普通函数不行;
//5)调用友元函数时,在实际参数中需要指出要访问的对象;
#include<iostream>
using namespace std;
class Point //Point类定义
{
public: //外部接口
Point(int xx = 0, int yy = 0)
{
x=xx;
y=yy;
cout<<"Construtor is called."<<endl;
}
~Point()
{
cout<<"Destroyed."<<endl;
}
friend void dis(Point &); //友元函数在类中的声明;
private:
int x, y;
};
void dis(Point &t)
{
cout<<"友元函数输出结果:"<<t.y-t.x<<endl; //中文输出乱码,重建一个文件就好了
}
int main( )
{
Point a(4, 5);
dis(a);
}
//类模板
#include<iostream>
using namespace std;
template<class T>//必要的声明 T是虚拟类型
class Com
{
private:
T a,b;//T
public:
Com(T aa = 0,T bb = 0)//T
{
a=aa;
b=bb;
}
void cmp()
{
if(a>b)cout<<a<<endl;
else cout<<b<<endl;
}
};
int main()
{
Com <int>d1(1,2);//构造函数 <实际类型>
d1.cmp(); //函数调用
Com <double>d2(1.11,2.22);
d2.cmp();
}
//运算符重载
//实现复数简洁的运算 a b直接表示复数加法
//函数类型 operator 运算符名 (形参列表){对运算符重载的处理}
//自己写个随便名字的函数也可以实现,其实,可能这是为了增强程序的可读性的一种方式吧,共识性的函数
#include <iostream>
using namespace std;
class complex // 类定义
{
double real, image;
public:
complex (double r = 0, double i = 0)
{
real = r;
image = i;
}
void show( )
{
cout<<real<<" "<<image<<"i"<<endl;
}
complex operator ( const complex &c2)//类名 operator 符号(类名 &c2) 其实传入了两个参数的地址,第一个其实是this默认传进去的 老师说加不加const,都判对
{
complex c;
c.real = real c2.real;
c.image = image c2.image;
return c ;
}
complex operator*(const complex &c2)//不加const 多目运算的时候可能会报错,出现有参数是临时变量的时候
{
complex c;
c.real = real * c2.real-image*c2.image;
c.image = image*c2.real real * c2.image;
return c ;
}
};
int main( )
{
complex a(1,3.1),b(1.2,2) ;
complex c=b;
a=b c; //双目运算,左值,右值
b=b c*a;//临时对象作为引用参数,形参必须是常量应用,也就是说此时形参左值可以是临时的,也可以是非临时的,形参右值,必须是非临时的,如果是临时的,那么要加const 运算结果是临时变量存储,这里运算符重载函数返回值也是临时变量存储的
c=a*b complex(1,2);
a.show();
b.show();
c.show();
}
//友元函数实现运算符重载的交换律
//重载函数作为类成员函数时,被重载的运算符左侧必须是一个对象 a b a必须是一个对象b不是该类对象,就不可交换
//友元函数,使重载预算符可以在某类私有成员和非私有之间运算,所以满足交换律
//运算符重载规则:
//"." ".*" "::" "sizeof" " ?:"条件运算符 这五个不可重载
//重载不会改变运算符优先级
//重载双目运算符 "==" ">" "<"
//要构建一个String 类,重载函数以友元函数的形式出现在类中,双目符重载函数作为成员函数报错
#include <iostream>
#include <string.h>
using namespace std;
class String
{
public:
String()
{
p=NULL;
} String(char *str);
friend bool operator>(String &s1,String &s2);
friend bool operator<(String &s1,String &s2);
friend bool operator==(String &s1,String &s2);
void display();
private:
char *p;
};
String::String(char *str)
{
p=str;
}
void String::display()
{
cout<<p;
}
bool operator>(String &s1,String &s2)
{
if(strcmp(s1.p,s2.p)>0) return true;
else return false;
}
bool operator<(String &s1,String &s2)
{
if(strcmp(s1.p,s2.p)<0) return true;
else return false;
}
bool operator==(String &s1,String &s2)
{
if(strcmp(s1.p,s2.p)==0) return true;
else return false;
}
int main()
{
String s1("Hello"),s2("Book"),s3("Computer");
cout<<(s1>s2)<<endl;
cout<<(s1<s3)<<endl;
cout<<(s1==s2)<<endl;
return 0;
}
//单目运算符 重载
Time operator ()//前置 i
{
sec-=60;//private:sec;
return *this;
}
Time operator (int)//对后置的重载 i
{
Time temp(*this);
sec-=60;//私有成员private:sec;
return temp;
}
//重载流插入和流提取运算符重载(理解)
//istream& operator>>(istream&,自定义类&);
//ostream& operator<<(ostream&,自定义类&);
//举例、
cout<<c3;
//想得到a bi的形式
class Complex
{
//……
public:
//……
friend ostream& operator<<(ostream& output,complex&c); //目前就写成友元形式才编译ok
};
friend ostream& operator<<(ostream& output,complex&c)
{
output<<"("<c.real<<" "<<c.image<"i)"<<endl;//以后只改这里就行了,对输出内容的重载
return output;
}
//转换构造函数
//这里整数转换为复数
#include <iostream.h>
class complex
{ private: double real,image;
public:
complex() { real = 0; image = 0; } // 普通构造函数
complex(double r) { real = r; image = 0; } // 转换构造函数,只能有一个参数
void show()
{ cout<<real;
if (image>=0) cout<<' '<<image<<'i'<<endl;
else cout<<image<<'i'<<endl; }
};
void main()
{ complex c1(3.6); // 调用转换构造函数
c1.show();
complex c2; // 调用普通构造函数
c2.show();
c2= complex (10.6); // 调用转换构造函数生成对象并赋值给c2.
c2.show();
}
//类型转换函数,(必须是一个类成员函数
operator double()
{
return real;
}
//转换构造函数和类型转换函数有一个共同的特点:
// 在需要的时候,系统会自动调用这些函数。
// 例如:若已定义double变量到 d1、d2,complex对象 c1,c2。且类中已定义了类型转换函数,
// 设程序有表达式: d1 = d2 c1;
// 编译系统将做如何处理呢?
// 编译系统发现“ ”左侧的d2是double型,右侧c1是 complex对象,如果没有对“ ”重载,就会检测有无类型转换函数,结果发现对double的重载函数,就调用该函数,将complex对象c1转换成double型数据,建立了一个临时的double型变量,并与d2相加,最后将double 型数据赋给变量d1。
//类型转换格式
//(int)a C风格
//int(a) C 同时保留了风格C
//继承和派生
//构造函数和析构函数不能继承
//派生类可以对从基类继承过来的成员函数覆盖(重新定义内容)
class person
{
};
class student : public person //公有继承(最多)
{
//基类的私有成员不能用派生类新定义的成员函数去访问,只能通过继承过来的成员函数去访问基类私有成员数据
//适合多级派生,其它两种多级派生会存在各种不可访问的情况
};
class student : private person //私有继承
{
//基类的成员数据、成员函数 都变成派生类的private类型
};
class student : protected person //保护继承
{
//基类的成员数据、成员函数 基类数据成员public都变成派生类的protected类型,private不变
//在基类、派生类中是一种public般的存在
//在类外,是private般的存在
};
//多重继承派生类的构造函数(构造函数参数表写法)
//格式如下:
// 派生类构造函数名(总参数表):
// 基类1构造函数 (参数列表1),
// 基类2构造函数 (参数列表2),
// 基类3构造函数 (参数列表3),…
// { 派生类中新增数据成员初始化语句 }
class CD:public CB,public CC
{
int d;
public:
CD(int n1,int n2,int n3,int n4)
:CC(n3,n4),CB(n2)
{ d=n1; cout<<"CD::d="<<d<<endl; }
~CD(){cout<<"CDobj is destructed"<<endl;}
};
//构造函数积累子类调用根据继承顺序调用的构造函数,所以构造函数参数表写的顺序最好是先写基类再写子类的构造函数(就是大括号前面的那几个构造函数)
//解释派生构造的例子
#include<iostream>
using namespace std;
class person
{
int x, y;
public:
person(int xx, int yy)
{
x = xx;
y = yy;
cout<<"person ok"<<endl;
}
void dplay()
{
cout<<x<<" "<<y<<endl;
}
};
class student :public person
{
int z;
person parent; //子对象:派生类中定义的对象
public:
student(int xx, int yy, int zz): //构造函数参数表: 1.参数表加冒号
person(xx,yy),parent(xx,yy) //2.写基类构造函数,子对象
{ //3.大括号,派生类中新增数据成员初始化语句
z = zz;
cout<<"student ok"<<endl;
}
void display()
{
dplay();//玩弄权限,内部调用,实现外部一次性调用
cout<<z<<endl;
}
};
int main()
{
student z(1,2,3);
z.display();
}
//派生关系二义性处理规则
//对子类而言,不加类名限定时默认为是处理子类成员;
//而要访问父类重名成员时,则要通过类名限定。
class person
{
void display()
{
}
};
class student:public person
{
void display()
{
}
};
int main()
{
student a;
a.display();//这调用的就是把父类覆盖后的那个
a.person::display();//这调用的就是父类的,覆盖后没有消失 加个 基类 作用域符
}
//单线多级派生
#include<iostream>
using namespace std;
class A
{
public://构造函数要设置为公有,养成习惯
A()
{
cout<<"A ok"<<endl;
}
~A(){cout<<"A over"<<endl;}
};
class B: public A
{
public:
B(): A()
{
cout<<"B ok"<<endl;
}
~B(){cout<<"B over"<<endl;}
};
class C : public B
{
public:
C()://构造函数涉及到的基类只有直接基类,不考虑间接
B()
{
cout<<"C ok"<<endl;
}
~C(){cout<<"C over"<<endl;}//写析构函数时就只需要考虑析构自己,基类析构与其无关,
};
int main()
{
C demo;
}
//cout保留小数位
#include <iomanip>
cout<<fixed<<setprecision(2)<<PI*r*r<<endl;
//虚基类
//存在的意义:解决多继承中存在的二义性
//例子:A派生B、A派生C、D继承了B、C ,此时A的成员都public,在D中修改a的成员数据,存在一个二义性,修改的是从B还是从C继承过来的A呢
class A
class B : virtual public A //虚继承
class C : virtual public A
class D : public B, public C //前面虚,这里不用加都虚
//虚拟以后,c.a就是唯一的了
//多态性
//利用虚函数机制实现了“一个接口(基指针),多种方法”的完备意义的多态性(可以借此实现对象链表)
//虚函数的作用,使基类的某函数内容不会过早的绑定,内容呈现动态化,一旦有新的定义,就可以在该派生类中用虚函数调用
//虚函数要和基指针结合使用
//基指针:基类类型指针,可以指向基类,也可以指向由它的派生类抽象而来的实例,但是也只能调用基类继承过来的函数
//只使用基指针调用派生类,并不会调用继承过来被覆盖的结果,还是会调用在基类中该函数的内容
#include <iostream>
class Pet //基类
{public:
void speak() { cout<<"How does a pet speak ?"<<endl; }
};
class Cat : public Pet //派生类Cat
{public:
void speak() { cout<<"miao!miao!"<<endl; }
};
class Dog : public Pet //派生类Dog
{public:
void speak() { cout<<"wang!wang!"<<endl; }
};
void main()
{ Pet p1,*ptr=&p1; //A
Dog dog1;
Cat cat1;
ptr->speak(); //B
ptr = &cat1;
ptr->speak(); //C
ptr = &dog1;
ptr->speak(); //D
}
//结果:
//How does a pet speak?
//How does a pet speak?
//How does a pet speak?
//虚函数要和基指针结合使用(可是实现基指针指向派生类对象时,可以调用该对象重新写后的函数内容)
//虚函数实现原理(每个类中都有一个虚表存放虚函数,一旦在某类被重新定义,该类虚表就会更新,指针调用的就是虚表里的函数)
#include <iostream.h>
class Pet //基类
{public:
virtual void speak() //虚函数
{ cout<<"How does a pet speak ?"<<endl; }
};
class Cat : public Pet //派生类Cat
{public:
void speak() { cout<<"miao!miao!"<<endl; }
};
class Dog : public Pet //派生类Dog
{public:
void speak() { cout<<"wang!wang!"<<endl; }
};
int main()
{ Pet p1,*ptr=&p1; //A
Dog dog1;
Cat cat1;
ptr->speak(); //B
ptr = &cat1;
ptr->speak(); //C
ptr = &dog1;
ptr->speak(); //D
}
//结果:
//How does a pet speak ?
//miao!miao!
//wang!wang!
//构造函数不可以是虚函数,析构函数可以
//
//抽象类
//指的是只用作派生,不用来定义对象的类
//只存在纯虚函数的类一定是抽象类
//纯虚函数形式
//virtual 函数类型 函数名( 参数表 ) =0;
//在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。
//如果基类中没有保留函数名,则无法实现多态性。
class Pet
{
//...
virtural ~pet(){}
};
class Cat
{
//...
};
int main()
{
Pet *p;
Pet a();
Cat b();
p=&a;
//...
p=&b;
//...
delete p;//基指针指向过基类、派生类,需要定义基类析构函数为虚函数,否则,执行该操作后,只释放基类内存,可能会内存泄漏
}
//多态性
//静态多态性:函数重载、运算符重载
//动态多态性:基指针和虚函数
//在c 中,用C语言中的printf和scanf进行输入输出,不对数据类型作合法性检查。漏写&不报错
//cout 和 cin 并不是C 提供的语句,它们是iostream类的对象
//iostream是 流类 cout、cin 是流对象
//格式输出的控制符(只对离自己最近的一个数据起作用)
//#include<iomanip>
a = 1;
cout<<dec<<a;//十进制结果
cout<<hex<<a;//16进制结果
cout<<oct<<a;//8进制结果
//这样更简单
cout<<setbase(10)<<10<<endl;//十进制结果
cout<<setbase(8)<<10<<endl;//16进制结果
cout<<setbase(16)<<10<<endl;//8进制结果
cout<<setw(n)<<10;
//设置输出字段宽度为10,字段不足10位,空格在左边填补
cout<<setw(n)<<setfill('*')<<10;
//10位字段,星号填补
cin>> //遇到空格、回车结束
char ch[20];
cin.get(ch,10,‘a’); // 读空格,读回车,从输入流中读取9个字符赋给ch或读到’a’结束
cin.get(ch,10); //读够九个字符结束
cin.getline(ch,20,'/');//和cin.get()区别就是,getline读到结束符结束,并跳过该结束符,下一次读的位置是结束符的下一位,cin.get下一次读的还是结束符,无法多次读
//自己补充的
string s;
getline(cin,s);//针对string类型的,读一行,读入空格,特好用
cout<<fixed<<setprecision(2)<<3.1415926<<endl;//保留两位小数
//异常处理
//越界、语法错误、输入类型错误、......
//try-catch throw
//try(在里面throw)、catch(抓到try 中throw 抛出的内容)必须紧跟在一起
try{
//代码区1
}
catch(//异常信息类型)
{
//代码区2,花括号不可省
}
catch(...)//三个点,表示可以捕获任意异常信息类型
{
//代码区2,花括号不可省
}
if(满足发生某种错误的条件) throw 变量a、其它自定义句子......;
#include <iostream>
using namespace std;
int main()
{
double m ,n;
cin >> m >> n;
try { //try提供检查区域
cout << "before dividing." << endl;
if( n == 0)
throw -1; //抛出int类型异常
else
cout << m / n << endl;
cout << "after dividing." << endl;
}
catch(double d) { //catch 捕获错误类型
cout << "catch(double) " << d << endl;
}
catch(int e) {
cout << "catch(int) " << e << endl;
}
cout << "finished" << endl;
return 0;
}
#include<iostream>
using namespace std;
double Div(double,double);
int main()
{
try
{
cout<<"7.3/2.0="<<Div(7.3,2.0)<<endl;
cout<<"7.3/1.0="<<Div(7.3,1.0)<<endl;
cout<<"7.3/0.0="<<Div(7.3,0.0)<<endl;
}
catch(double)
{
cout<<"b not good"<<endl;
}
cout<<"ok"<<endl;
}
double Div(double a,double b)
{
if(b==0.0)
throw b;
return a/b;
}
//命名空间
//解决名字冲突
//
//
//
//
//
//修改控制台标题
#include<windows.h>
int main()
{
SetConsoleTitle("班级 学号");
}
//cout是ostream中的对象,cin是istream中的对象
//cerr标准错误流,不经过缓冲区,直接报错误信息
//clog标准错误流,经过缓冲区,缓冲区满或者遇到endl时才输出错误信息。
代码语言:javascript复制//打开文件
//在定义文件流对象时指定参数(常用)
例:ostream outfile (“f1.dat”, ios::out );
//打开文件并指定输入输出方式
//文件输出例子
#include<fstream>
ofstream outfile("/home/z/zz.txt",ios::out | ios::app);
if(!oufile)
{
cerr <<"open error"<<endl;
exit(1);
}
outfile<<a<<endl;
outfile.close();
//文件输入例子
void messageIntput()
{
ifstream infile("d:\data\Manager.dat",ios::in);
if(!infile)
{
cerr<<"open error before read"<<endl;
exit(1);
}
int id;
string identity,name,sex,birth;
double salary;
cout<<"导入中......"<<endl;
while(infile>>identity)
{
infile>>id;
infile>>name;
infile>>birth;
infile>>sex;
infile>>salary;
}
cout<<"员工号:"<<id<<endl;
cout<<"姓名:"<<name<<endl;
cout<<"职位:"<<identity<<endl;
cout<<"出生年月:"<<birth<<endl;
cout<<"性别:"<<sex<<endl;
cout<<"实发工资:"<<salary<<endl;
cout<<"=========="<<endl;
infile.close();
cout<<"导入完成"<<endl;
}
//补充1
//c 中的null 0 nullptr
//因为C 中不能将void *类型的指针隐式转换成其他指针类型,而又为了解决空指针的问题,所以C 中引入0来表示空指针,这样就有了类似上面的代码来定义NULL。实际上C 的书都会推荐说C 中更习惯使用0来表示空指针而不是NULL,尽管NULL在C 编译器下就是0。
//补充2
对象指针初始化过程中,目前只有初始为0不报错,
null在c中本质是个void* ,在c 中是个0,nullptr显示not declare,暂时不知道哪里出了问题
//补充3
//employee *p =0;常常会报错,要分开写
employee *p ;
p =0;
//补充4
c string与数字拼接
方法一:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
int a = 2;
string b = "abc";
stringstream ss;
ss << a << b;
cout << ss.str() << endl;
return 0;
}
//疑问5
show()函数是个虚函数,基指针指向后,执行不了,,??详见小实验副本(对象链表)
//疑问6
基指针可以指向派生类其它非虚函数吗,否 ,但可以指向基类所有函数
//疑问7
manager(int i, string na, string bi, string se, string ty, double sa):
employee(i,na,bi,se,ty)
第一行的 bi se 顺序颠倒,,按理来说不影响,结果影响了
//补充8
delete p;
p指向一个对象的时候,编译器发出警告,可能会出现未知行为,这时候,构造函数变成虚函数,就不报错了,保证都能删光
//教训9
做对象链表时,刚开始四种情况,全用了单链表,后来改成循环链表,结果,就改了一种情况,想了半天,忘了还有其他代码区域情况没改,
//疑问10
写重载函数的时候,所有的重载函数好好地,改完别的函数,就全崩掉了,直接不访问了,重新打开CB运行,又好了??啥情况
//收获11
路径表示中的“”也要用双斜线”\”,c 中\是一种转义字符,他表示一个,就像n表示回车一样。所以C 中的路径名:D:matcom45docuser_themesm.dat应为:
CString filename=_T(“D:\matcom45\doc\users\_themes\m.dat”);
或
CStringfilename=_T(“D:/matcom45/doc/users/_themes/m.dat”);
“./” 加不加都一样,就是指当前目录
“../” 表示当前目录的上级目录,即当前的父目录。
//收获12
(转载)C ofstream和ifstream详细用法(关于从文件输入的理解)
https://www.cnblogs.com/mints/p/8759879.html
//收获13
c 输出文件文件路径中,不能出现不存在的文件夹
//收获14
//sort函数
#include <iostream>
#include <algorithm>
using namespace std;
bool cmp(int a,int b)
{ return a > b;}
int main()
{
int a[] = {5,3,7,3,9,4};
sort(a,a 6,cmp); //第三个参数这里写了,从大到小,不写默认从小到大 ,a 6,说明有6个数据,最后一个数据是存储在a[5]
for(int i = 0;i < 6;i )
{
cout<<a[i]<<" ";
}
return 0;}
//sort给结构体排序
//Sort(start,end,排序方法)
#include<iostream>
#include<cstdio>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
struct E{
char name[101];
int age;
int score;
}buf[1000];
//分数,姓名,年龄按照升序进行排列
bool cmp(E a, E b)
{
if (a.score != b.score)
return a.score < b.score;
int tmp = strcmp(a.name,b.name);
//对于strcmp函数,strcmp(str1,str2),相等时返回0,str1 > str2时返回正数,否则返回负数
if (tmp != 0)
return tmp < 0;
else
return a.age < b.age;
}
int main()
{
int N;
while (scanf("%d", &N) != EOF)
{
for (int i = 0; i < N; i )
scanf("%s%d%d",buf[i].name,&buf[i].age ,&buf[i].score);
sort(buf,buf N,cmp);
for (int i = 0; i < N; i )
printf("%s %d %dn",buf[i].name,buf[i].age,buf[i].score);
}
return 0;
}
//收获15
//由于友元的使用会破坏类的封装,因此从原则上说,要尽量将运算符函数作为成员函数。但考虑到各方面的因素,一般将单目运算符重载为成员函数,将双目运算符重载为友元函数。
//考试漏洞16
//常量五大类:整型数字、浮点数字、字符、字符串和布尔值
//考试漏洞17
//返回值不同,是不可以作为重载的依据
方 式 | 作 用 |
---|---|
ios :: in | 以输入方式打开文件 |
ios :: out | 以输出方式打开文件。若文件存在,则清除原内容 |
ios :: app | 以输出方式打开文件。写入的数据增加在文件末尾 |
ios :: ate | 打开已有文件,文件指针指向文件末尾 |
ios :: trunc | 打开文件,若文件存在,则删除原数据 |
ios :: binary | 以二进制方式打开文件 |
ios :: in | ios :: out | 以读写方式打开文件 (组合方式) |
ios::out | ios :: binary | 以二进制方式打开输出文件(组合方式) |
ios :: in | ios :: binary | 以二进制方式打开输入文件(组合方式) |