C++入门笔记

最近在学习C++,附上自己学习过程中的一些笔记,有些问题在别的博客答得很好,直接给出了链接,有些写了一些自己的理解。长期更新。

1.C++中类与对象详解——解剖类中涉及的各种函数

构造函数

创建新对象的时候被调用
默认是内联函数

析构函数

特殊的成员函数,在删除对象的时候被调用。无参数,无返回值。~classname();

拷贝构造函数

类有指针变量并且有动态分配内存必须有一个拷贝构造函数。
举例:

class Line
{
public:
    int getLength(void);
    Line(int len);             // 简单的构造函数
    Line(const Line& obj);      // 拷贝构造函数
    ~Line();                     // 析构函数
private:
    int* ptr;
};

Line::Line(const Line& obj)
{
    cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷贝值
}
友元函数

类的定义中要有友元函数的原型且在前面假friend;不是成员函数;可以类的所有成员包括private和protected成员。

内联函数

就是在函数名前加上inline,然后别的使用方法和普通函数一样,但是不可以有循环语句。特点是在编译阶段会把函数调用处替换成函数体;也就是用空间换时间。

类的静态成员函数和静态成员

类的定义中不能初始化静态变量,但可以在类的外面写:
int Box::objectcoun=0;这样初始化;
静态成员函数只能调用静态成员变量,没有this指针。

类的静态类型和动态类型

类由于继承导致对象的指针和引用具有两种不同的类型: 静态类型 和 动态类型 。
静态类型 :指针或者是引用声明时的类型。
动态类型 :由他实际指向的类型确定。
例如:

GameObject *pgo=new SpaceShip;  
 //pgo静态类型是 GameObject *动态类型是 SpaceShip*
Asterioid *pa = new Asterioid; 
//pa的静态类型是 Asterioid *,动态类型也是 Asterioid *
pgo = pa; //pgo静态类型总指向 GameObject *
          //动态类型指向了 Asterioid *
GameObject &rgo = *pa; //rgo的静态类型是 GameObject
                       //动态类型是 Asterioid

原文链接:https://blog.csdn.net/harry_lyc/article/details/6123475

类的概念中其他一些小点

(1)类中没有申明变量的权限,那么默认是private的。

2.继承与多态

1.派生类创建与删除的过程:
调用基类构造函数
调用派生类构造函数
调用派生类析构函数、
调用基类函数
2.多态
https://blog.csdn.net/qq_39412582/article/details/81628254

3.::作用域符的作用

(1)当局部变量和全局变量同名时,在全局变量前面加上::可以区分全局变量和局部变量。如果不加符号修饰,默认该函数内对变量操作都是对局部变量的操作。
如对于程序:
int count;
int main() {
int count;
count = 5;
::count = 10;
cout << "Global Variable: "<<::count << endl;
cout << "Loacal Variable: "<<count << endl;
return 0;
}
输出:
在这里插入图片描述
(2)成员函数在类外面进行申明
C++为例避免不同的类有名称相同的成员而采用作用域的方式进行区分;格式:<s数据类型><类名>::<成员函数名>(){}。
例如:
class animals {
public:
string name;
string getName() {};
};
string animals::getName() {
return name;
}

4.const 关键字总结

这篇博客讲述的很清楚,不再赘述。
https://blog.csdn.net/u011333734/article/details/81294043

5.C++文件操作总结

打开文件的方式在ios类(所以流式I/O的基类)中定义,有如下几种方式:

名称方式
ios::out为输入(读)而打开文件
ios::out为输出(写)而打开文件,不存在时创建文件;每次打开先清空文件
ios::ate初始位置:文件尾
ios::app所有输出附加在文件末尾
Column 1Column 2
ios::trunc如果文件已存在则先删除该文件
ios::binary二进制方式

这些方式是能够进行组合使用的,以“或”运算(“|”)的方式。
写文件的一个例子:
在这里插入图片描述
可以在文件夹project1中看到music.txt,打开后看到如下内容。

如果重新打开文件,把输出到文件的部分代码改成:
file << “Green Thumb” << endl;
重新打开文件看到如下内容:在这里插入图片描述之前的内容被清除,要想追加写入内容,要用ios::ate.
另外,执行代码的时候文件时不要打开文件。

读文件的例子:
在这里插入图片描述
在这里插入图片描述

6.C++中的引用&和C中的&什么区别

C++中的&:引用,即某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。引用的声明方法:类型标识符 &引用名=目标变量名。
如:

int a; int &ra=a;
 //定义引用ra,它是变量a的引用,即别名

说明:
(1)&在此不是求地址运算,而是起标识作用
(2)类型标识符是指目标变量的类型。
(3)声明引用时,必须同时对其进行初始化。(int &ra ;是错误的)
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。 ra=1; 等价于 a=1;
(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等
(6)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
常引用

常引用声明方式

const 类型标识符 &引用名=目标变量名;用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。
如:

int a ;
const int &ra=a;
ra=1; //错误
a=1; //正确

7.动态联编和静态联编详解

这里有一篇博客写的很详细,我现附上链接:
https://blog.csdn.net/neiloid/article/details/6934129
我重新排版整理了下。

在C++中,联编是指一个计算机程序的不同部分彼此关联的过程。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。

(1) 静态联编

静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数调用)与执行该操作代码间的关系,确定这种关系称为束定,在编译时的束定称为静态束定。静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高,但灵活性差。
下面举一个栗子~

#include< iostream >
class A
{public:
 	voidf()
 		{cout<<"A"<<"";}
};

classB:publicA
{public:
	 voidf()
		 {cout<<"B"<<endl;}
};
void main()
{
	A*pa=NULL;
	A a;
	B b;
	pa=&a;
	pa->f();
	pa=&b;
	pa->f();
}

该程序的运行结果为:A A

从运行结果可以看出,**通过对象指针进行的普通成员函数的调用,仅仅与指针的类型有关,而与此刻指针正指向什么对象无关。**要想实现当指针指向不同对象时执行不同的操作,就必须将基类中相应的成员函数定义为虚函数,进行动态联编。

(2)动态联编

动态联编是指联编在程序运行时动态地进行,根据当时的情况来确定调用哪个同名函数,实际上是在运行时虚函数的实现。这种联编又称为晚期联编,或动态束定。动态联编对成员函数的选择是基于对象的类型,针对不同的对象类型将做出不同的编译结果。**C++中一般情况下的联编是静态联编,但是当涉及到多态性和虚函数时应该使用动态联编。**动态联编的优点是灵活性强,但效率低。

动态联编规定,只能通过指向基类的指针或基类对象的引用来调用虚函数,其格式为:指向基类的指针变量名->虚函数名(实参表)或基类对象的引用名.虚函数名(实参表)

实现动态联编需要同时满足以下三个条件:
① 必须把动态联编的行为定义为类的虚函数。
② 类之间应满足子类型关系,通常表现为一个类从另一个类公有派生而来。
③ 必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。
下面举一个栗子

#include"iostream.h"
class A
{
public:
	virtual voidf()//虚函数
	{
		cout<<"A"<<"";
	}
};

class B : public A
{
public:
	virtual void f()//虚函数
	{
		cout<<"B"<<endl;
	}
};

void main()
{ 
	A*pa=NULL;
	A a;
	B b;
	pa=&a;
	pa->f();
	pa=&b;
	pa->f();
	return 0;
}

该程序的运行结果为:A B

从运行结果可以看出,将基类A中的函数f定义为虚函数后,当指针指向不同对象时执行了不同的操作,实现了动态联编。

(3)动态联编分析

动态联编要求派生类中的虚函数与基类中对应的虚函数具有相同的名称、相同的参数个数和相同的对应参数类型、返回值或者相同,或者都返回指针或引用,并且派生类虚函数所返回的指针或引用的基类型是基类中虚函数所返回的指针或引用的基类型的子类型。如果不满足这些条件,派生类中的虚函数将丢失其虚特性,在调用时进行静态联编。

通过指向基类的指针来调用虚函数
#include< iostream >
class base
{
public:
		virtual void fun1()
		{cout<<"base fun1"<<endl;}
		
		virtual void fun2()
		{cout<<"base fun2"<<endl;}
		
		void fun3()
		{cout<<"base fun3"<<endl;}
		
		void fun4()
		{cout<<"base fun4"<<endl;}

};

Class derived:public base
{
public:
	virtual void fun1()
	{cout<<"derived fun1"<<endl;}
	
	virtual void fun2(intx)
	{cout<<"derived fun2"<<endl;}
	
	virtual void fun3()
	{cout<<"derived fun3"<<endl;}
	
	void fun4()
	{cout<<"derived fun4"<<endl;}
};

int main()
{
	base* pb;
	derived d;
	pb=&d;
	//通过指向基类的指针来调用虚函数
	pb->fun1();
	pb->fun2();
	pb->fun3();
	pb->fun4();
	return  0;
}

该程序的运行结果:

Derived fun1 base fun2 base fun3 base fun4

本例中函数fun1在基类base和派生类derived中均使用了关键字virtual定义为虚函数,并且这两个虚函数具有相同的参数个数、参数类型和返回值类型。因此,当指针pb访问fun1函数时,采用的是动态联编。
函数fun2在基类base和派生类de-rived中定义为虚函数,但这两个虚函数具有不同的参数个数。函数fun2丢失了其虚特性,在调用时进行静态联编。
函数fun3在基类base中说明为一般函数,在派生类derived中定义为虚函数。在这种情况下,应该以基类中说明的成员函数的特性为标准,即函数fun3是一般成员函数,在调用时采用静态联编。
函数fun4在基类base和派生类derived中均说明为一般函数,因此基类指针pb只能访问base中的成员。

通过基类对象的引用来调用虚函数
#include< iostream >
class CPoint
{
public:
	CPoint(doublei,doublej)
		{x=i;y=j;}
	
	virtual double Area()
		{return 0.0;}
private:
	double x,y;
};

class CRectangle : public CPoint
{
public:
	CRectangle(double i,double j,double k,double l);

	Double Area(){return w*h;}

private:
	double w,h;
};

CRectangle::CRectangle(double i,double j,double k,double l):CPoint(i,j)
{  w=k;h=l;  }

void fun(CPoint &s)
{ 
 cout<<s.Area()<<endl;
 }
//通过基类对象的引用来调用虚函数
int main()
{
	CRectangle rec(3.0,5.2,15.0,25.0);
	fun(rec);
}

该程序的运行结果为:375

例中的成员函数Area在基类CPoint中使用了关键字virtual定义为虚函数,在派生类CRectangle中定义为一般函数,但是进行了动态联编,结果为15*25即375。这是因为一个虚函数无论被公有继承多少次,它仍然保持其虚特性。在派生类中重新定义虚函数时,关键字virtual可以写也可不写,但为了保持良好的编程风格,避免引起混乱时,应写上该关键字。

小结

从以上四个例子中可以看出:虚函数是实现多态的基础,是实现动态联编的必要条件之一。动态联编要靠虚函数来实现,虚函数要靠动态联编的支持。两者相辅相成,缺一不可。

8.重载中的那些事儿

(1)函数重载
函数名不通,形式参数类型必须不同,返回类型可以同可不同。
(2)运算符重载
重载C++的内置算法。比如:

Box operator+(const Box&);
//operator关键字,+是要重载的运算符,
// 重载 + 运算符,用于把两个 Box 对象相加
      Box operator+(const Box& b)
      {
         Box box;
         box.length = this->length + b.length;
         box.breadth = this->breadth + b.breadth;
         box.height = this->height + b.height;
         return box;
      }

(3)不能被重载的内置运算符
不能被重载的运算符只有五个,分别是:

.(成员访问运算符)
*(成员指针访问运算符)
::(域运算符)
sizeof(长度运算符)
?:(条件运算符)

前两个运算符不能重载是为了保证访问成员的功能不被改变 ,域运算符和sizeof运算符的运算对象是类型而不是变量或者一般表达式,不具备重载的特征。

9.类模板和模板类

附上学习链接(转)https://blog.csdn.net/low5252/article/details/94654468

10.编程踩坑系列

1.编程常常犯的低级错误:
引用对象的成员函数记得加()。
2.C++ STL:
accumulate(s.begin(),s.end(),initial) 累加

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值