搜索
您的当前位置:首页正文

【C++入门教程】学习笔记3

来源:好走旅游网

内联函数(inline function)

宏函数,用空间换时间

//宏函数缺陷1:需要加括号
#define myAdd(x,y) ((x)*(y));
//缺陷2:即使加了括号,但也可能与预期不符合
#define myCompare(a,b) ((a)<(b)?(a):(b))
void test01(){
	int a=10;
	int b=20;
	int ret=myCompare(++a,b);//由于宏函数的性质,++a调用了两次
	cout<<ret<<endl;
}
//为了避免宏缺陷,所有有了内联函数
基本概念

内联函数是个真正的函数,不会有宏函数的缺陷,但是会在必要的时候按照宏函数的模式运行

inline int myCompare2(int a,int b)
inline int myCompare2(int a,int b){return a<b?a:b;}
类内部的成员函数自动为内联函数,隐藏了inline关键字
内联函数和编译器

内联函数只是对编译器的一个建议,编译器不一定接受这种建议,并且就算不写inline,编译器也会对适合内联的函数加inline关键字


函数提高

函数默认参数

函数形参可以有默认值

int func(int a,int b=20,int c=30){
	return a+b+c;
}
int main(){
	cout<<func(10,30);
	//如果传了参数,那么是用传入的,而不是初始化的默认值
	return 0;
}

注意事项:

  1. 如果某个位置有默认参数,那么他后面都要有
  2. 如果函数声明有默认参数,那么函数实现就不能有->声明和实现只能有一个有默认参数
占位参数
  1. 必须要传入占位参数
  2. 目前阶段的占位参数用不到
  3. 占位参数可以用默认参数
void func(int a,int =10){
	cout<<"this is func"<<endl;
}
int main(){
	func(10,1);
	return 0;
}
函数重载

函数名可以相同,提高代码的复用性
函数重载的满足条件:

  1. 在同一个作用域下
  2. 函数名称相同,函数返回类型也要相同
  3. 函数的参数类型不同或者个数不同或者顺序不同
    注意事项:
  4. 引用作为重载的条件
  5. 函数重载碰到默认参数
void func(int &a){
	cout<<"func(int &a)"<<endl;
}
void func(const int &a){
	cout<<"func(const int &a)"<<endl;
}
void func2(int a,int b=10){
	cout<<"func2(int a,int b)"<<endl;
}
void func2(int a){
	cout<<"func2(int a)"<<endl;
}
int main(){
	func(1);//调用const int &a
	int a =10;
	func(a);//调用int &a
	const int b=10;
	func(b);//调用const int &a
	func2(1);//出现二义性。尽量避免
	return 0;
}
函数重载的实现原理

编译器可能会对函数名进行修饰,比如
void func() _func
void func(int a) _func_int
void func(int a,char c) _func_int_char
所以可以实现函数重载

extern C 浅析

如果用C++的编译方式,可能会将函数修饰,再去调用
解决方式:

  1. 利用C语言的方式

    extern "C" void show()//删掉头文件
    
  2. 在.h文件中加上几行代码

    #ifdef __cpluscplus
    extern "C"{
    #endif
    
    
    #ifdef __cpluscplus
    }
    #endif
    

类和对象

C++面向对象的三个特性:封装、继承、多态

封装

意义:

  1. 将属性和行为作为一个整体
  2. 将属性和行为加以权限控制
类的基本操作
const double pi =3.14;
class Circle{
public:
	int m_R;
	double calC(){
		return 2*pi*m_R;
	}
};

int main(){
	Circle c;
	c.m_R = 10;
	cout<<c.calC()<<endl;
}
访问权限
  1. public 公共权限 类内可以访问,类外也可以访问

  2. private 私有权限 类内可以访问,类内不可以访问,不可继承

  3. protected 保护权限 类内可以访问,类内不可以访问,可以继承

    class Person{
    public:
    	string name;
    protected:
    	string car;
    private:
    	string password;
    public:
    	void init(){
    		name="zhang";
    		car="BMV";
    		password="123445";
    	}
    };
    int main(){
    	Person p;
    	p.name="lin";
    	//p.car="1";不可访问,会报错
    }
    
struct和class的区别

默认的访问权限不同
struct默认为public
class默认为private

成员属性设置为私有
class Person{
public://提供接口
	void set_name(string new_name){
		name=new_name;
	}
	string get_name(){
		return name;
	}
	void set_age(int new_age){
		if(new_age<0)age=0;
		else if(new_age>150)age=0;
		else age=new_age;
	}
	int get_age(){
		return age;
	}
	void set_lover(string lover_name){
		lover=lover_name;
	}
private:
	string name;//可读可写
	int age;//可读
	string lover;//可写
};
int main(){
	Person p;
	p.set_name("zhang");
	cout<<p.get_name()<<endl;
}

可以自己控制读写权限
可以控制写入的有效范围

class Cube{
public:
	void set_L(int L){m_L=L;}
	void set_W(int W){m_W=W;}
	void set_H(int H){m_H=H;}
	int get_L(){return m_L;}
	int get_W(){return m_W;}
	int get_H(){return m_H;}
	int get_V(){return m_L*m_W*m_H;}
	int get_S(){return 2*(m_L*m_W+m_L*m_H+m_W*m_H);}
	bool is_equal(Cube &a){
		if(m_L!=a.get_L())return false;
		if(m_W!=a.get_W())return false;
		if(m_H!=a.get_H())return false;
		return true;
	}
private:
	int m_L;
	int m_W;
	int m_H;
};
bool isSame(Cube &a,Cube &b){
	if(b.get_L()!=a.get_L())return false;
	if(b.get_W()!=a.get_W())return false;
	if(b.get_H()!=a.get_H())return false;
	return true;
}
int main(){
	Cube a,b;
	a.set_L(10);
	a.set_W(10);
	a.set_H(10);
	b.set_L(10);
	b.set_W(10);
	b.set_H(10);
	cout<<isSame(a,b)<<endl;
	cout<<a.is_equal(b)<<endl;
}

对象的初始化和清理

如果自己不写,编译器会提供,只是是空的

构造函数
  1. 没有返回值不写void
  2. 函数名称和类名相同
  3. 构造函数可以有参数,也可以重载
  4. 会自动调用
析构函数
  1. 没有返回值也不写void
  2. 函数名和类型相同,在名称前加~
  3. 不可以有参数,因此不可重载
  4. 在销毁前会调用,只调用一次
class Person{
public:
	Person(){
		cout<<"use"<<endl;
	}
	~Person(){
		cout<<"use"<<endl;
	}
private:
	string name;
	int age;
	string lover;
};

int main(){
	Person p;
}
构造函数的分类和调用

按参数分:

  1. 有参构造2. 无参构造
    按类型分:
  2. 普通构造4. 拷贝构造
    调用的方式:
    括号 显式 隐式转换法
int main(){
	//括号法
	Person p;//Person p()错误
	Person p1(10);
	Person p2(p1);

	//显示法
	Person p;
	Person p1=Person(10);
	Person p2=Person(p2);//对于右侧的匿名对象,改行结束后,内存会被释放

	//隐式转换法
	Person p4 = 10;//Person p1=Person(10);
	
}

注意事项:
调用默认构造时注意不要带括号,因为编译器会把加了括号的Person p()当做函数声明
不要利用拷贝函数去初始化一个匿名对象

拷贝构造函数的调用时机
  1. 使用一个已经创建完毕的对象初始化一个新对象
  2. 值传递的方式给函数参数传值
  3. 值方式返回局部对象
void doWork(Person p){}
Person doWork2(){
	Person pp;
	return pp;
}
int main(){
	Person p1(10);
	Person P2(p1);
	Person p;
	doWork(p);//在将实参传给形参时会用拷贝构造函数构造
	doWork();//将pp用构造函数拷贝再返回
}

#####构造函数调用规则
默认情况下,只要写了class就会提供
默认构造函数,默认拷贝构造函数,默认析构函数
如果用户定义了有参构造函数,那就不提供无参构造,但提供拷贝构造
如果用户定义了拷贝构造函数,不提供其他构造函数

深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区申请空间,进行拷贝操作(new delete)
如果利用编译器的拷贝构造函数,会做浅拷贝操作
析构时先进后出,重复析构,则会重复释放
浅拷贝造成了重复释放,利用深拷贝解决

class Person{
public:
	Person(){}
	Person(int a,int h){
		age=a;
		height= new int(h);
	}
	Person(const Person &p){
		age=p.age;
		//height = p.height;//默认的浅拷贝
		height=new int(*p.height);//深拷贝
	}
	~Person(){
		//将堆区的数据释放
		if(height!=nullptr){
			delete height;
			height=nullptr;
		}
	}
	int age;
	int *height;
};

int main(){
	Person p1(10,100);
	Person p2(p1);


}
初始化列表
	Person():age(0),height(80){}
	Person(int a,nt b):age(a),height(b){}
类对象作为类成员

类中的成员可以使另一个类的对象,构造的顺序

class A{
};
class B{
	A a;
};
//先构造A再构造B,先析构B再析构A
explicit (明确的)关键字

防止隐式法构造,括号法和显示法

this指针

this指针指向被调用的成员函数所属的对象

  1. 解决名称冲突
  2. 返回对象本身
class Person{
public:
	int age;
	Person(int age){
		//age=age;//错误,变量名冲突
		this->age=age;
	}
	Person& PersonAddAge(Person &p){//如果用值的方式返回,就不是返回到p2,而是创建了一个新的对象
		this->age+=p.age;
		return *this;
	}
};
int main(){
	Person p1(10);
	Person p2(0);
	//p2.PersonAddAge(p1);
	p2.PersonAddAge(p1).PersonAddAge(p1);
	cout<<p2.age<<endl;
}
空指针访问成员函数
class Person{
public:
	void showClassName(){
		cout<<"this is person class"<<endl;
	}
	void showPersonAge(){
		if(this==nullptr)return;
		cout<<this->age<<endl;//因为传入的是空指针,访问属性所以报错
	}
	int age;
};
int main(){
	Person* p = nullptr;
	p->showClassName();
	p->showPersonAge();
}
const修饰成员函数
友元
重载运算符

左移运算符

class Person{
	friend ostream& operator << (ostream &cout,Person &p);
public:
	Person(int a,int b):A(a),B(b){}
private:
	int A;
	int B;
	//成员函数重载,一般不用,cout再右边

};
ostream& operator << (ostream &cout,Person &p){
	cout<<p.A<<" "<<p.B<<endl;  
}
int main(){
	Person p(10,10);
	cout<<p;
}

因篇幅问题不能全部显示,请点此查看更多更全内容

Top