内联函数:当编译器访问的时候,把函数定义的语句直接插入到主语句。减少内存开销。
定义方法:
- inline + 成员函数
- 整个函数体出现在类定义内部。
class B{
inline void func1(); //利用inline,定义内联函数。
void func2(){};//整个func2()的函数放在类的内部也标志着内联函数。
};
void B::func1(){}//具体定义可以放在类外。
函数的重载:
成员函数–带缺省参数。
#include <iostream>
using namespace std;
class Location{
private :
int x,y;
public:
void init(int x = 0, int y = 0);
void valueX(int val) {x = val;}//一个有参数
int valueX(){return x;}//一个没参数。
//valueX()就是函数的重载
};
void Location :: init(int X, int Y){
x = X;
y = Y;
}
int main(){
Location A;
A.init(5);//这个函数里面的y就使用了系统默认的y值,0。
A.valueX(5);
cout << A.valueX();
//上面两个valueX()调用的是不同的函数
return 0;
}
但也要防止程序的二义性。如:
void valueX(int val = 0){x = val;}
int valueX(){return x;}
A.valueX();
这个A.valueX()就无法判断该执行哪个。就会报错。
构造函数:
成员函数的一种,
- 名字与类名相同,可以有参数,不能返回值(void也不行)
- 作用是对对象进行初始化,如给成员变量赋初值。
- 如果定义类时没写构造函数,则编译器生成一个默认无参数的构造函数。
- 对象生成时,构造函数自动被调用。对象一旦生成,就再也不能在其上执行构造函数。
- 一个类可以有多个构造函数。
个人猜测:就是赋予初始化的参数,python中的那些初值的定义在__init__里面定义的,但cpp没有,就来个构造函数。
构造函数的作用:
- 初始化工作,有了这个就不用专门再写初始化函数。(那个set_啥啥啥的)
- 有时对象没被初始化就使用,会导致程序出错。
来一个默认无参的构造函数:
class Complex{
private:
double real,imag;
public:
void Set(double r, double i);
}; // 编译器自动生成默认的构造函数
Complex c1;
Complex *pc = new Complex; // 也生成了对象,所以也用了默认的构造函数初始化。
来一个自己定义的构造函数:
class Complex{
private:
double real, imag;
public:
Complex( double r, double i = 0);
};
Complex :: Complex(double r, double i){
real = r; imag = i;
}
//这时候如果来一个:
Complex c1;
// 程序就会报错。因为缺少构造函数的参数。
Complex *pc = new Complex; //报错。
Complex c1(2); //这个是对的。
Complex *pc = new Complex(3,4); //也是对的。
来个多个构造函数的:
class Complex{
private:
double real, imag;
public:
void Set(double r, double i);
Complex(double r, double i);
Complex(double r);
Complex(Complex c1, Complex c2);
};
Complex:: Complex(double r, double i)
{
real = r; imag = i;
}
Complex:: Complex(double r)
{
real = r; imag = 0;
}
Complex:: Complex(Complex c1, Complex c2);
{
real = c1.real + c2.real;
imag = c1.imag + c2.imag;
}
Complex c1(3),c2(1,0),c3(c1,c2);
上面三个构造函数是重载的,根据调用时候的参数决定。
所以 c1 就是实部3,虚部0。吊用了第二个。
c2是调用了第一个,c3调用了第三个。
构造函数与对象数组。
class CSample{
int x;
public:
CSample(){
cout << "Constructor 1 Called" << endl;
}
CSample(int n){
x = n;
cout << "Constructor 2 Called" << endl;
}
};
int main(){
CSample array1[2]; // 生成了两个无参的对象
cout << "step1" << endl;//输出两个 Constructor 1 Called
CSample array2[2] = {4,5};//构造了两个有参初始化的对象。
cout << "step2" << endl;
CSample array3[2] = {3};
cout << "step3" << endl;
CSample *array= new CSample[2]; // 没有交代参数,所以是无参。
delete []array4;// new出来的要收回。
return 0;
class Test{
public:
Test(int n){}//1
Test(int n, int m){}//2
Test(){}//3
}
Test array1[3] = {1,Test(1,2)};
//三个元素分别使用了第一个,第二个,第三个初始化。
//因为第一个是直接给了一个变量1,第二个是直接告诉他用Test(1,2),第三个是空的。
Test array2[3] = {Test(2,3),Test(1,2),1};
//用的是2 2 1
Test *parray3[3] = {new Test(4), new Test(1,2)};
// 指针数组对象可以不初始化,那么对象也没有被创建,因为它仅仅是指针。
// 必须使用这种 new + 对象名的形式,用new出来对象的地址,来初始化对象。
//但这个只生成了两个对象。最后一个元素 pArray[2] 没有对象生成。
1. 复制构造函数:
基本点:
- 只有一个参数
- 形如
X::X(X&)
或X::X(const X&)
后者能用常量对象作为参数。 - 如果没有定义复制构造函数,编译器会生成默认的复制构造函数。
如:
class Complex{
private:
double real,imag;
};
Complex c1;
Complex c2(c1);
有一个默认的无参构造函数,还有另一个默认的复制构造函数。将c2初始化称和c1一样。
如果自己定义了一个复制构造函数,那么默认的就不会存在。
class Complex{
public:
double real,imag;
Complex(){}
Complex (const Complex & c){
real = c.real;
imag = c.imag;
cout << "Copy Constructor called";
}
};
Complex c1;
Complex c2(c1);
不能有形如 X:X(X)的构造函数,也就是说参数不能是同类的对象。(所以才有复制构造函数)
基本作用:
-
初始化语句:
Complex c2(c1);
Complex c2 = c1; -
如果某个函数有一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用。
class A
{
public:
A();
A(A&a){
cout << "Copy constructor called" << endl;
}
};
void fun(A a1){}
int main(){
A a2;
fun(a2);
return 0;
}
这里的a1的复制构造函数是我们自己写的,任务并不是复制,所以形参a1的值就不会等于a2,程序输出结果为"Copy constructor called"。
如果我们自己没有写复制构造函数,使用系统默认的,那么a1的内容就会是a2拷贝。
3. 如果函数的返回值是类A的对象时,则函数返回时A的复制构造函数被调用。
class A
{
public:
int v;
A(int n){v = n;};
A(const A&a){
v = a.v;
cout << "Ccc" << endl;
}
};
A Func(){
A b(4);
return b;
}
int main(){
cout << Func().v << endl;
return 0;
}
上程序的输出结果为
Ccc
4
上面那个Ccc是定义的复制构造函数的输出,下面的4是真正的Func().v。
2. 类型转化构造函数:
目的:实现类型的自动转换。
特点:只有一个参数,不是复制构造函数。
class Complex{
public:
double real,imag;
Complex(int i){ //类型转换构造函数。和构造函数同名。
cout << IntConstructor called" << endl;
real = i; imag = 0;
}
Complex(double r, double i){
real = r; imag = i;
}
};
int main(){
Complex c1(7,8);
Complex c2 = 12; //对c2进行初始化(不是赋值)不会生成一个临时对象,只会将12作为参数,传给Complex
c1 = 9; // 这是一个赋值语句。编译器不会报错,因为编译器会调用类型转换构造函数,把9作为实参调用Complex(int i)函数。
//把9自动转换成一个临时Complex对象。
cout << c1.real << "," << c1.imag << endl;
return 0;
}
析构函数:
成员函数的一种,
名字与类名相同,
在最前面加 “~”,
没有参数和返回值,
一个类最多只有一个析构函数。
定义类时没写析构函数,则编译器生成缺省析构函数。
class String{
private:
char *p;
public:
String(){
p = new char[10];
}
~String();
};
String :: ~String(){
delete []p; //对一个数组,要在前面用方括号如果只是 delete p;,那么智慧释放一个一个对象
}
class Ctest{
public:
~Ctest(){cout << "called" << endl;
};
int main(){
Ctest array[2];
cout << "end main" << endl;
return 0;
}
结果时先输出"end main",因为有两个数组元素的对象,所以会出现两次“called"。
delete运算会导致析构函数的调用:
Ctest *pTest;
pTest = new Ctest; //构造函数调用
delete pTest; //析构函数调用
-----------------------
pTest = new Ctest[3]; //构造函数调用3次
delete []pTest; //析构函数调用3次。
例子:
class Demo{
int id;
public:
Demo(int i)
{
id = i;
cout << "id = "<< id << "Constructed" << endl;
}
~Demo()
{
cout << "id = " << id << "Destructed" << endl;
}
};
Demo d1(1); //全局变量,会调用一次构造函数。
void Func(){
static Demo d2(2); //静态,所以会在程序结束消亡
Demo d3(3); //在函数结束时消亡。
cout << "Func" << endl;
}
int main(){
Demo d4(4); //会输出 id = 4 Constructed
d4 = 6; // 会调用系统默认的类型转化构造函数,所以会出现 id = 6 Consturcted 和 id = 6 Destructed
cout << "main" << endl;
{ Demo d5(5);} //只会作用在花括号内。所以也是出现 id = 5 Constructed 和 id = 6 Destructed
Func();
cout << "main ends" << endl;
return 0;
}
最终输出结果为:
id = 1 Constructed
id = 4 Constructed
id = 6 Constructed
id = 6 Destructed
main
id = 5 Constructed
id = 5 Donstructed
id = 2 Constructed
id = 3 Constructed
Func
id = 3 Destructed
main ends
id = 6 Destructed
id = 2 Destructed
id = 1 Destructed
注意,最先构造的对象会最后消亡。
静态成员变量,静态成员函数
前面加了 static 关键字的成员。
对象中的静态成员对象被所有对象共享。不属于固定某一个。
sizeof 不会加上静态成员变量的大小。
访问静态成员:
类名::成员名 访问静态成员。CRectangle::PrintTotal();
对象名.成员名 CRectangle; r.PrintTotal();
(但他并不属于r这个对象。)
指针 -> 成员名CRectangle *p = &r; p->PrintTotal();
引用.成员名 CRectangle & ref = r; int n = ref.nTotalNumber;
静态成员变量本质上是全局变量。
目的是把和某些类紧密相关的全局变量和函数写到类里面,增加可读性和修改性。
静态成员变量要单独拿出来声明一下。
如:
int CRectangle::nTotalNumber = 0;
int CRectangle::nTotalArea = 0;
在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数。(因为非静态成员函数不属于任何一个对象,所以它也不知道w到底是属于谁的,就会报错。)
CRectangle::CRectangle(int w_,int h_)//构造函数
{
w = w_;
h = h_;
nTotalNumber ++;
nTotalArea += w*h;
}
CRectangle::~CRectangle()//析构函数
{
nTotalNumber --;
nTotalArea -= w*h;
}
void CRectangle::PrintTotal()
{
cout << nTotalNumber << "," << nTotalArea << endl;
}
上代码容易出错,因为CRectangle类可能是复制构造函数生成的。(就是以该对象作为函数的参数,函数的返回值的情况。),没有增加总数(没有经过构造函数)却减少了总数(因为调用了析构函数)。
解决方法就是编写复制构造函数,让CRectangle类在复制构造函数中除了赋值还要增加总数和增加总面积。
成员对象:(之前提到的都是成员变量和成员函数)
成员对象是一个类的成员变量是另一个类的对象。
包含成员对象的类叫做封闭类。(类和类是能交织一起的)
class CTyre{ //轮胎类
private:
int radius;
int width;
public:
CTyre(int r, int w):radius(r),width(w){} //构造函数。这样的初始化方式更好看一些。
};
class CEngine{ //轮胎类
};
class CCar{
private:
int price;
CTyre tyre;
CEngine engine;
public;
CCar(int p, int tr, int tw);
};
CCar :: CCar(int p, int tr, int w):price(p),tyre(tr,w){ // 构造函数。
};
int main(){
CCar car(20000,17,225);
return 0;
}
如果CCar类不定义构造函数就会出错。因为编译器不知道car.tyre该如何初始化。
当封闭类对象生成时,先执行所有成员对象的构造函数,再执行封闭类的构造函数。与成员初始化列表中出现的顺序无关。
封闭类的对象消亡时,先执行封闭类的析构函数,再执行成员对象的析构函数。(先生成的后消亡)
友元:
友元函数:
一个类的友元函数可以访问该类的私有成员。
class CCar; //要在CDriver之前声明一下。
class CDriver{
public:
void ModifyCar(CCar * pCar);
};
class CCar{
private:
int price:
friend int MostExpensiveCar(CCar cars[], int total); //这个函数是成员函数之外的函数,声明为友元。
friend void CDriver::ModifyCar(CCar *pCar);//声明其他类的成员函数为友元。
};
void CDriver::ModifyCar(CCar *pCar)
{
pCar->price += 1000; //因为声明过为友元,所以可以访问CCar里的price。
}
int MostExpensiveCar(CCar cars[],int total)
{
int tmpMax = -1;
for( int i = 0; i<total; ++i)
if(car[i].price > tmpMax)
tmpMax = cars[i].price;
return tmpMax;
}
int main()
{
return 0;
}
注意,将一个类的成员函数作为另一个类的友元时,是包括构造,析构函数的。(就是上面才CCar中的CDriver的modify那个函数)
友元类:
如果A是B的友元类,那么A的成员函数能访问B的私有成员。
如:
class CCar{
private:
int price;
friend class CDriver;
};
class CDriver{
public:
CCar myCar;
void ModifyCar(){
myCar.price += 1000;
}
};
int main(){return 0;}
这个函数中在CCar类中声明了CDriver为友元类,所有在CDriver类中就能访问到CCar类中的私有成员price。
但友元类之间不能传递。
this指针:
指向成员函数所作用的对象。
class Complex{
public:
double real,imag;
void Print(){ cout << real << "," << imag;}
Complex(double r,double i):real(r),imag(i)
{}
Complex AddOne(){
this -> real++; //等价于real++
this -> Print();//等价于Print();
return *this; //相当于返回操作后的原对象。
}
};
int main(){
Complex c1(1,1),c2(0,0);
c2 = c1.AddOne();
return 0;
} //输出2,1
class A
{
int i;
public:
void Hello(){cout << "Hello" << endl;}
}; //机器翻译后是 void Hello(A *this){cout << "Hello" << endl;
int main()
{
A *p = NULL;
p->Hello();
}
//这个程序不会出做,会输出Hello。
//因为p->Hello();会被翻译成Hello(p),然后就相当于调用了一次Hello(p),会输出"Hello"虽然p指针式空的。
但是下面这个就会出错:
class A{
int i;
public:
void Hello(){cout << i << "Hello" << endl;}
};
int main(){
A *p = NULL;
p -> Hello();
静态成员函数不能使用this指针。
因为静态成员函数并不能具体作用于某个对象。
所以静态成员函数的真是参数的个数就是程序中写出参数的个数。
常量对象、常量成员函数、常引用:
-
如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加const关键字。
-
在类的成员函数说明后面可以加const关键字,则该成员函数称为常量成员函数。
常量成员函数执行期间不应修改其所作用的对象。因此,在常量成员函数中不能修改成员变量的值(静态成员变量除外),也不能调用同类的非常量(静态成员函数除外)。
class Sample{
public:
int value;
void GetValue() const;
void func(){};
Sample(){}
};
void Sample::GetValue() const
{
value = 0; //error 因为常量成员函数不能修改作用的变量的值。
func(); //出错,不能调用其他的非常量成员函数。因为它可能改变对象的值。
}
int main(){
const Sample o;
o.value = 100;//error 常量对象不能被修改
o.func(); //error 常量对象上面不能执行非常量成员函数。
o.GetValue(); //可以,常量对象上可以执行常量成员函数。
return 0;
}
如果两个成员函数,名字和参数都一样,但是一个是const,另一个不是,算重载。
class CTest{
private:
int n;
public:
CTest(){n = 1;}
int GetValue const {return n;}
int GetValue {return 2*n}
}
int main(){
const CTest obj1;
CTest obj2;
cout << obj1.GetValue() << "," << obj2.GetValue();
return 0;
//输出结果一个是:1 2
引用前加const关键字,成为常引用。
对象作为函数参数,生成参数对象调用复制构造函数,效率低。指针作参数不够美观。
所以用对象的引用作为参数。但函数中修改形参同时也会修改外面对象的值,所以可以用常引用作为参数,如:
class Sample{...};
void PrintfObj(const Sample & o){...}
//语句中不会出现修改o的值的语句。