1.复制构造函数:
eg:
class abc
{
public:
abc(int z,int c)
{
x = z; y = c;
}
abc(abc const &);//复制构造函数
private:
int x; int y;
};
abc::abc(abc const &p)
{
x = p.x;
y = p.y;
}
void abc::out() {
cout << "x=" << x << ",y=" << y << endl;
}
int main()
{ abc a(1,2);//建立第一个对象
abc b = a;//调用复制构造函数
abc c(b);//调用复制构造函数
}
2.浅复制和深复制:
若类中没有指针类型,那系统自带的浅复制足够。若是有指针类型,则浅复制复制的是变量的地址,会导致在最后delete的时候释放多次,程序崩溃,因此需要用户自定义一个深复制来防止这一情况的发生。
深复制为:创建一个新的变量,将原变量的值赋值到该变量上,然后再将新的对象里的指针变量指向该变量,导致有两个地址,因此不会崩溃。
eg://意思一致。
int i = 1;
int *p = &i;
cout << "&p=" << p << endl;
int x = *p;
cout << "x=" << x << endl;
int *y = &x;
cout << "&y=" << y << endl;
3.静态函数与静态成员:
静态成员要在类内声明,类外定义。而静态函数里面的成员只能有静态成员,因此静态成员和静态函数是类内不同对象通用的,因此全是静态成员才不会导致可能出现的错误,并且静态函数可以类内定义。
修改办法:mutable 类型 成员变量 eg:mutable int a;
友元:
可以访问所有成员,包括protected,private,public,并且友元是单向的。分为友元类和友元函数,无非就是在一个类中的public里面声明的时候在最前面加一个friend。
eg:
友元类://在定义两个类的时候顺序无所谓,可以让处在上面的友元下面的。
class abc
{
public:
private:
abcd g;
int x=g.a; int y;
};
class abcd {
friend class abc;
private:
int a = 1;
};
友元函数:
class abcd {
friend void acm();
private:
int a = 1;
};
void acm() {
abcd h;
cout << h.a << endl;
}
注意:不可以在另一个类中声明该类中的函数friend另一个类,要想使该类的函数可使用另一个类的private成员,必须friend 类。
4.运算符重载:
【1】:成员函数重载:
注意:++和--这种有分为前置和后置,因此参数表内如果在最后加了个int,则为后置。
eg:void operator++(int);//++为自增,不需要参数,因此直接来个int。需要参数的话直接在括号内的最后加个int即可。eg:(int x,int)//(参数,参数,...,int)
定义格式:类型 类名::operator op(参数表);//类型:返回类型。op:运算符
{
//操作
}
声明格式:类型 operator op(参数表);
【2】:用成员或友元函数重载运算符:
注意:有些会加个&。eg:abc::abc &operator++();返回类型不会void,为类的话,为return *this;//this是个指针,表明指向该对象。我觉得&是为了保证改的确实是那个对象的那个参数,地址那种感觉。
成员函数:
一元运算:Object.operator op();二元运算:Object.operator op(Object.a);
友元函数:
一元运算:operator op(ObjectB b);//一个类型的对象
二元运算:Object.operator op(ObjectB a,ObjectB b);
注意:友元函数不可以重载 = 和 () 和 [ ] 和 -> 。
重载赋值运算符:
//赋值运算符用于对象数据的复制,只能用成员函数重载,且不能被继承。
格式:类名 &类名::operator=(类名); 一定要用&
eg:Name &Name::operator=(Name);
重载运算符[ ]和():
//只能用成员函数重载。
(1):重载下标 [ ] 运算符://为二元运算符
eg:声明:int &X::operator[ ](int);//X为一个类。 一定要用&
调用:x.operator[ ](k);//x为X类的对象。
(2):重载函数调用运算符()://为二元运算符
eg:声明:int A::operator()(int,int);//A为类。
调用:a(x,y)//a为类A的对象。被解释为:a.operator()(x,y);
重载流插入<< 和 流提取>>运算符:
(1):重载流插入<<://cout 一定要用&
//第一个ostream为返回类型 ,()里面的&是为了确保一定改的是这个对象。
//const 是为了保证引用的对象不会被改变。 Vector为数据类型。
声明:ostream &operator<<(ostream &output,const Vector &);
定义:ostream &operator<<(ostream &output,const Vector &A)
{
for(int i=0;i<A.len;i++){output<<A.v[i]<<"i"<<endl;}
return output;
}
(2):重载流提取>>://cin 一定要用&
//第一个istream为返回类型 ,()里面的&是为了确保一定改的是这个对象。
//const 是为了保证引用的对象不会被改变。 Vector为数据类型。
声明:istream &operator>>(istream &iutput,const Vector &);
定义:istream &operator>>(istream &iutput,const Vector &A)
{
for(int i=0;i<A.len;i++){iutput>>A.v[i]<<"i"<<endl;}
return iutput;
}
5.类类型转换:
不过就是前面的运算符重载里面的参数类型不同,然后通过内部的运算进行转化而已。
6.继承://C++可以继承多个类,Java只能继承一个父类,但是可以有多个接口
class A {
public:
void abc() {
cout << " A" << endl;
}
protected:
int x = 1;
};
class B :public A{
public:
void abc() {
cout << " B" << endl;
}
protected:
int x = 1;
};
int main() {
A a;//A的对象a
B b;//B的对象b
a.abc();//调用a的abc函数
b.abc();//基类的重名函数会被屏蔽,所以调用的是b的abc函数
b.A::abc();//这就是在b中调用A的abc函数的方法
}
初始化顺序和结束顺序:
初始化: 顺序
1.基类的构造 2.子类的构造
结束顺序:
3.子类的析构 4.基类的析构
7.虚继承:
就是共用一个父类。
定义方式:class A :virtual public B;//A类虚继承B
class C :virtual public B;//C类虚继承B
A类和C类公用B类的成员。
8.多态,也包括了虚函数:
静态多态:函数地址早绑定,编译的时候就已经确定了函数地址。
动态多态:函数地址晚绑定,运行的时候才确定函数地址。父类的指针或引用指向子类对象。说明了下面为什么要用&。//指针都是四个字节。
以下为动态多态:
正常情况下:
class A {
public:
void speak() { cout << "A is speaking" << endl; }
};
class B :public A{
public:
void speak() { cout << "B is speaking" << endl; }
};
void ALLspeak(A &a) //这个&是关键,如果没有&,那调用结果还是A的speak()。
{
a.speak();
}
int main() {
B b;
ALLspeak(b);
}
由于C++的特性,该ALLspeak()可以调用,但是调用的话因为类型为A,所以输出的是A的speak(),而不是B的speak()。此时我们的函数地址是早绑定的,但此时我们真正的目的是调用子类B的speak(),因此需要使用多态使函数地址晚绑定,以输出B的speak()。
此时的操作为将父类A的speak()变为虚函数:
//这样,必须父类和子类的该函数返回类型和名字一致。
virtual void speak()
{ cout << "A is speaking" << endl; }
//此时该函数还是可以直接用调用的,但是若变为纯虚函数,则无法调用,导致该类无法实例化。
eg:
A a;//A类中含有抽象类。
a.speak();
此时会根据speak(参数)的参数类型决定调用该对象的speak(),此时为地址晚绑定,在运行使才绑定函数地址。
纯虚函数和抽象类:
因为以上情况的父类的函数压根就不用,所以干脆直接不需要实现该虚函数的功能,因此可以改为纯虚函数:virtual 返回值类型 函数名(参数列表)=0;
eg:virtual void speak()=0;
但是Java的为abstract void speak()即可,不需要等于0。
而有纯虚函数的类就是抽象类。
抽象类特点:
(1).无法实例化对象。
(2).子类必须重写该抽象类中的纯虚函数,否则也变成抽象类。
虚析构和纯虚析构://Java没有析构函数,因为它会自动释放。
多态使用的时候,如果子类有指针类型,开辟到堆区,那么释放的时候无法调用子类的析构函数,而导致子类对象内存泄漏。eg:
A *a=new B;//跟Java一样,左父右子。
a ->speak();
delete a;//此时不会释放子类的析构函数,父类构造析构都正常,子类的构造函数也可以调用。
因此需要虚析构和纯虚析构来解决这个问题。
//如果有了纯虚析构,则该类属于抽象类,无法实例化对象,跟纯虚函数一样。
解决办法:虚析构
把父类A的析构函数改为虚析构即可。
eg:virtual ~A(){};
//虚析构也会被调用,纯虚析构也一样,因为可能父类中也有成员变量在堆区。
而纯虚析构为:virtual ~A()=0;
在类内要virtual ~A()=0;//但是可以在类外定义它,防止父类内存泄漏。
eg:A::~A(){ };
9.模板: (类模板和函数模板)
//template <class T1,class T2,......>要紧跟下面的类或者函数,不能分开。
目的:制造一些东西可以通用。
模板说明格式:template<class T1,class T2,.......>//T1,T2为类型。
函数模板://可以重载
eg:定义
template<class T>
T max(T a,T b){
return a>b?a:b;
}
调用:
int a=1,b=2;
max(a,b);
类模板:
eg:定义
template<class T>
class abc{
public:
T MAX()
{
return x>y?x:y;
}
protected:
T x,y;
};
写文件读文件之类的:
/*
文件打开方式:
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置:文件尾
ios::app 追加方式写文件
ios::trunc 如果文件存在先删除,再创建
ios::binary 二进制方式
打开方式可以配合使用。eg: ios::binary|ios::out
用 |
fstream A;
A.open("测试文件.txt", ios::out);
A << "石家庄" << endl;
A << " 一加一等于二" << endl;
A.close();
fstream B;
B.open("测试文件.txt", ios::in);
char g[1024] = { 0 };
/*while (B>>g)
{
cout << g << endl;
}
while (B.getline(g, sizeof(g)))
{
cout << g << endl;
};
B.close();
*/
一些好玩的小函数://现在使用的是中文标点符号,需要到时改成英文标点符号。
system(“pause”);//按任意键继续
system(“cls”);//自动清空cmd显示器
abort();//退出程序
return 0;//直接结束全部程序