从C语言到C++

面向对象特征:
抽象,多态,继承,封装

1.条件编译

#if 0       #if 1     #ifdef    #ifndef
...         ...			...		...
#endif      #else	  #endif	#else
	       ...					...
            #endif 				#endif
***********/

2.输入输出流,名字空间

using namespace std;
using std::cin;
std::cin>>i;

3.访问和内部作用域变量同名的全局变量,要用全局作用域限定 ::

#include <iostream> 
using namespace std;
double a = 128;
int main (){
   double a = 256;
   cout << "Global a: " <<::a << endl;  //::是全局作用域限定    
   return 0;
}

4. 通过 try-catch处理异常情况 :正常代码放在try块,catch中捕获try块抛出的异常

     try {                        //放置正常代码,但可能出现问题
      if (a > 100) throw 100;   //throw 抛出异常(也称为抛弃异常),100为异常对象,出现异常则下面的语句不会执行
      if (a < 10)  throw 10;    //throw后下面语句便不执行了
      throw "hello";
   }
   catch (int result) {         //捕获异常
    ........
   }
   catch (char * s) {            //捕获字符串
	 ........
   }

5. 引用(别名),引用主要用于函数形参,C语言函数形参都是值参数

int a = 3, &r = a;   //r就是a
void swap(int &x, int &y) {  //利用别名,形参x就是实参a
	int t = x;
	x = y;
	y = t;
}

6.默认形参

void print(char ch, int n = 1) {   //n为默认形参,默认形参必须在右边。n若不传递参数就为1,传递则是传递的数
	for (int i = 0; i < n; i++)
		cout << ch;
}

7 函数重载 :允许有同名函数存在,只要形参不同,注意:不能根据返回类型区分同名函数

函数重载,函数重写,同名隐藏
https://blog.csdn.net/inter_peng/article/details/53940179
https://blog.csdn.net/u014725884/article/details/47213651

8函数模板,类模板

template<typename T>   //函数模板,  typename 也可写成class
T add(T x, T y) {
	return x + y;
}
int main() {
// cout << add<int>(5, 3) << endl; 
	cout << add(5.3, 7.8) << endl;
	cout << add((double)5, 7.8) << endl; //歧义性   ,//函数模板是不允许隐式类型转换的,调用时类型必须严格匹配
}
//类模板
template<class T>
class Array {
	T size;
	T *data;
public:
	Array(int s) {   //构造函数
		size = s;
		data = new T[s];
	}
	virtual ~Array() {
		delete[] data;
	}
};

9.string,vector,迭代器iterator

string类具体操作: https://www.cnblogs.com/X-Do-Better/p/8628492.html

//vector
#include <iostream> 
#include <vector>    // vector是一个类模板
using namespace std;
int main() {	
	vector<int> v = { 7, 5, 16, 8 }; 
	  vector<int >  V2;
	v.push_back(25);    //push_back(),最后添加一个元素  	//成员函数size()、下标运算符[]   
    int	 i = v.size(); 
	v.pop_back();    //删除最后元素
	v.resize(2);

}
string::const_iterator cii;    //const迭代器,不能修改,  iterator能修改  string::iterator cii;

10.new/delete

int *p = new int;              delete p;   //防止内存泄漏
int *p = new int[n];			delete[] p; 

11. this指针: 指向类对象自己,成员函数实际上隐含一个this指针

#include <iostream> 
#include <string> 
using namespace std;

struct student {
	string name;	   	double score;
	void print() {
		cout << this->name << " " << this->score << endl;
	}
};
int main() {
	student stu;
	stu.name = "Li Ping";	   stu.score = 78.5;
	stu.print();   // print(&stu);   
}

12.访问控制、类接口

struct和class区别: struct里的成员默认是public(公开的) class里的成员默认是private(私有的)
接口:public的公开成员(一般是成员函数)称为这个类的对外接口,外部函数只能通过这些接口访问类对象,
private等非public的包含内部内部细节,不对外公开,从而可以封装保护类对象!

13.构造函数 ,拷贝构造函数,析构函数

构造函数是和类名同名且没有返回类型的函数,在定义对象时会自动被调用,而不需要在单独调用专门的初始化函数如init
构造函数用于初始化类对象成员,包括申请一些资源,如分配内存、打开某文件等
如果不写构造函数,编译器会默认生成一个String(){ }

析构函数是在类对象销毁时被自动调用,用于释放该
为了防止内存泄漏的发生,最好将基类的析构函数写成virtual虚析构函数。

默认的“拷贝构造函数”是“硬拷贝”或“逐成员拷贝”,指针指向同一块内存区域,当多次释放同一块内存就出错了!

#include <iostream> 
using namespace std;
class String {
	char *data; //C风格的字符串 
	int n;    //字符个数
public:
	~String() {
	..........
		if(data)
			delete[] data;
	}

	String(const String &s) {   // 重定义拷贝构造函数  硬拷贝{data=s.data;	s=s.n},
	                             //执行默认拷贝构造函数时s3和str2指向同一个地址,改变s3那么str2也改变
		cout << "拷贝构造函数!\n";	
               //当需要申请资源时,后需要将资源释放掉,最好重新定义拷贝构造函数,防止析构时多次释放
		data = new char[s.n + 1];		
		........
	}

	String(const char *s=0) {   //构造函数 ,:函数名和类名相同且无返回类型的成员函数
	. . . . .
		}
  ............
};

ostream& operator<<(ostream &o, String s) {
	for (int i = 0; i < s.size(); i++)
		cout << s[i];
	return o;
}

void f() {
	String str,str2("hello world");
	str2[1] = 'E';
//	cout << str2 << endl; 

	String s3 = str2; 			//拷贝构造函数   执行默认拷贝构造函数时s3和str2指向同一个地址,改变s3那么str2也改变
	cout << str2 << endl;  //str2传递给s调用ostream& operator<<(ostream &o, String s) 时 会默认调用拷贝构造函数
                           //调用时s和str2指向同一个地址,结束后调用析构函数s销毁,
						   //当f函数结束时,str2会销毁,造成了同一个内存多次销毁   所以要自定义拷贝构造函数 
}

14.运算符重载:针对用户定义类型重新定义运算符函数

运算符与普通函数在调用时的不同之处是:对于普通函数,参数出现在圆括号内;而对于运算符,参数出现在其左、右侧。

Complex a, b, c;   
…  
c = Add(a, b); // 用普通函数  
c = a + b;   // 用运算符+   
class Point{
	double x, y;
public:	
	double operator[](int i) const{ 	//const函数   。无法给通过下标赋值,为了和下面不一样。加了const,返回x复制品
		if (i == 0) 		return x;
		else if (i == 1)    return y;
		else throw "下标非法!"; 	//抛出异常     
	}
	double& operator[](int i) {   //下标可修改,返回x本身
		if (i == 0) return x;
		else if (i == 1) return y;
		else throw "下标非法!"; 	//抛出异常     
	}
	Point(double x_,double y_)       //构造函数
		x = x_; y = y_;
	}
	Point operator+(const Point q) {    //作为类的内部函数第一个参数就是调用操作本身。
		return Point(this->x+q[0],this->y + q[1]);
	}			
	friend ostream & operator<<(ostream &o, Point p);    //友元函数   
	friend istream & operator>>(istream &i, Point &p);
};

ostream & operator<<(ostream &o, Point p) {
	o <<p.x << " " << p.y<< endl;
	return o;
}
istream & operator>>(istream &i, Point &p) {
	i >> p.x >> p.y;
	return i;
}
/* Point operator+(const Point p,const Point q) { return Point(p[0] + q[0], p[1] + q[1]); } */

int main() {
	Point p(3.5, 4.8),q(2.0,3.0);	 //自动调用构造函数
	cout << p;
	cout << p[0] << "-" << p[1] << endl; //p.operator[](0)   
	p[0] = 3.45; p[1] = 5.67;
	cout << p<<q;	
	Point s = p + q; //p.operator+(q) 内部调用vs operator+(p,q)外部调用
	cout << s;
}

友元函数:为了使其他类的成员函数直接访问该类的私有变量。即:允许外面的类或函数去访问类的私有变量和保护变量,从而使两个类共享同一函数。
实际上具体大概有下面两种情况需要使用友元函数:(1)运算符重载的某些场合需要使用友元。(2)两个类要共享数据的时候

15. Inheritance继承(Derivation派生): 一个派生类(derived class)

多重继承: 从一个类派生出多个不同的类
多重派生: 从多个不同的类派生出一个类来
. 从1个或多个父类(parent class) / 基类(base class)继承,即继承父类的属性和行为,但也有自己的特有属性和行为.

#include <iostream> 
#include <string> 
using namespace std;
class Employee{
	string name;
public:
	Employee(string n);
	void print();
};
class Manager: public Employee{    //子类
	int level;
public:
	Manager(string n, int l = 1);
	//void print();
	};

Employee::Employee(string n) :name(n)//初始化成员列表  
{
	//name = n; 	
}
void Employee::print() {
	cout << name << endl;
}

Manager::Manager(string n, int l) :Employee(n), level(l) {
}
//通过调用基类的构造函数对基类成员初始化
//派生类的构造函数只能描述它自己的成员和其直接基类的初始式,不能去初始化基类的成员。 
Manager::Manager(string n, int l) : name(n), level(l) {  //错的
}
int main() {
	Manager m("Zhang",2);
	Employee e("Li");
	m.print();
	e.print();
}
//子类和父类有同名函数
class Manager : public Employee
{
	int level;
public:
	Manager(string n, int l = 1);
	void print();
};
Manager::Manager(string n, int l) :Employee(n), level(l) {
}
void Manager::print() {
	cout << level << "\t";
	Employee::print();
}
int main() {
	Manager m("Zhang");
	Employee e("Li");
	m.print();     //打印的是manage的print
	e.print();
}

派生类的指针可以自动转化为基类指针

int main() {
	Employee *p;
	Manager m("Zhang", 1);
	p = &m;
	p->print();   //此时打印的是employee中成员不是manage的
}

16.虚函数Virtual Functions,多态

上面代码可以将print声明为虚函数Virtual Functions :用virtual关键字,根据实际指向的类型输出,多态性,否则指针输出的是基类
静态成员函数不能是虚函数。

1.基类中必须包含虚函数(在成员函数之前加上virutal 关键字),并且在派生类中对基类中的虚函数进行重写
2.在派生类中重写的函数在基类中必须是虚函数(派生类中可以加virtual 关键字,不加的时候仍然会保持虚函数特性,但是建议加上)
3.派生类中虚函数必须与基类中虚函数的原型保持一致(返回值,函数名,参数列表和返回值都要相同,如果不同则会构成同名隐藏)
例外:析构函数(函数名不同)
例外:协变:基类的虚函数返回基类的引用或指针,派生类的虚函数返回派生类的引用或指针
4.基类与派生类中函数的访问权限可以不同,不过基类中虚函数必须是public权限
5.通过基类的指针或者引用来调用虚函数
6.静态成员函数不能定义为虚函数

多态:对于通过基类指针调用基类和派生类中都有的同名、同参数表的虚函数的语句,编译时并不确定要执行的是基类还是派生类的虚函数;而当程序运行到该语句时,如果基类指针指向的是一个基类对象,则基类的虚函数被调用,如果基类指针指向的是一个派生类对象,则派生类的虚函数被调用。这种机制就叫作“多态。

17.纯虚函数和抽象类

主要作用:相当于上述的基类,抽象类专门用来存放多态的接口,它是所有要实现多态的类的基类。
一 般的抽象类只是为了构成多态,并且其中的纯虚函数只是为了在其它的类种进行重写。
如果一个类继承于一个虚函数,那么这个函数种必须要重写抽象类中的纯虚函数。
写法:只有public的成员函数,并且只是声明,而且声后面加上 = 0,此函数叫做纯虚函数。
构成:以纯虚函数作为成员函数的类叫做抽象类,抽象类不能直接实例化,但是抽象类的指针是可以定义的。

18.内联函数:inline

内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。

void Foo(int x, int y);   
inline void Foo(int x, int y) // inline与函数定义体放在一起  
{   
…  
}   

对于任何内联函数,编译器在符号表里放入函数的声明(包括名字、参数类型、返回值类型)。如果编译器没有发现内联函数存在错误,那么该函数的代码也被放入符号表里。在调用一个内联函数时,编译器首先检查调用是否正确(**进行类型安全检查,或者进行自动类型转换,**当然对所有的函数都一样)。如果正确,内联函数的代码就会直接替换函数调用,于是省去了函数调用的开销。这个过程与预处理有显著的不同,因为预处理器不能进行类型安全检查,或者进行自动类型转换。假如内联函数是成员函数,对象的地址(this)会被放在合适的地方,这也是预处理器办不到的。

  C++ 语言的函数内联机制既具备宏代码的效率,又增加了安全性,而且可以自由操作类的数据成员。所以在C++ 程序中,应该用内联函数取代所有宏代码,“断言assert”恐怕是唯一的例外。assert 是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况。为了不在程序的Debug版本和Release版本引起差别,assert 不应该产生任何副作用。如果assert是函数,由于函数调用会引起内存、代码的变动,那么将导致Debug版本与Release版本存在差异。所以assert 不是函数,而是宏。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值