c++笔记

目录

一、指针笔记

二、引用的笔记

三、动态开辟内存空间

四、类 

1、类的多态

2、类的构造函数跟析构函数

3、虚析构:

五、模板

六、深拷贝与浅拷贝

七、运算符重载:


一、指针笔记

1、常量指针:

语法:【const int * p2 = &a;】 。指向的值不能改,指向可以改

2、指针常量:

语法:【int * const p1 = &a;】 指向不能改,指向的值可以改

3、Const 即修饰指针又修饰常量:

语法:【const int * const p3=&a;】指向、指向的值都不可以改

4、指针数组

语法【 返回值类型 (*数组名[ ]) 】 【黄色这里好像可有可无】(因为()与[ ]优先级相同,不括起来就相当于是元素返回值类型为指针的函数数组,所以要括起来)、(本质是数组,数组里存着指针)

5、数组指针

语法【int (*p1)[len]=arr;】//因为数组名就是地址,普通的指针就可以。(指向数组地址的指针,本质是指针

6、指针函数

语法:【数据类型 * 函数名(参数表) { 函数体} 】(返回值是指针的函数,本质是函数。数据类型是返回指针的类型)

例:利用指针函数来返回局部变量的值:

int * func()
{
int * p = new int(10);//动态内存分配在堆区,想删用delete。
return p;//指针函数的返回值是指针
}
int main()
{
int * a =func();
cout<<*a;

return 0;
}
//结果就是10

7、函数指针

语法:【数据类型(*函数名)(int x)】(int x 可有可无)【没有带“体”,因为是指向函数,不用管函数定义】(是指向函数的指针,本质是个指针)、(注意:指向函数的指针没有++和—运算)

8、结构体指针

指针用 -> 来访问数据成员。例:p1->name;

二、引用的笔记

语法: 数据类型 & 别名 = 对象

三、动态开辟内存空间

语法 : new 数据类型  

相当于开辟了一段存储这种数据类型变量(或者对象)的空间,要用一个指针去接收它,例如

int * p = new int(10);

动态开辟内存的数据存储再堆区,

delete p;//则删除堆区上的数据。

(delete [] p;//p是数组名。删除开辟的数组)

四、类

1、类的多态

(多态设好处是可读性强,想实现一些新功能可以在后面加,而不影响前面代码。)

动态多态【地址晚绑定】,(绑定地址)在运行阶段发生。
父类中(写了个虚函数)存储的其实是指针,叫虚函数(表)指针,指向一个虚函数表,表内存储的是父类中虚函数的地址。当子类重写父类的虚函数时,子类中的虚函数表 内部 会替换成 子类的虚函数地址(没重写则继承父类)。(重写指  函数返回值类型、形参列表、函数名 完全相同)
父类指针或引用指向子类对象的时候,因为接口是子类的对象,所以调用函数时会先在子类虚函数表中找这个函数的地址,找到则走子类重写的函数的地址。

例:

#include<iostream>
using namespace std;
//利用多态设计一个计算机类
class calculator
{
public:
	virtual int getresult()
	{
		return 0;
	}
	int num_1;
	int num_2;
};

class add :public calculator
{
public:
	int getresult()
	{
		return num_1 + num_2;
	}
};

class multiply : public calculator
{
public:
	int getresult()
	{
		return num_1 * num_2;
	}
};
void test01()
{
	calculator* p = new add;
	p->num_1 = 12;
	p->num_2 = 10;
	cout<< p->num_1 <<"+"<< p->num_2<<"="<< p->getresult()<<endl;

	delete p;//释放堆区的数据
	p = new multiply;//p仍是父类指针,然后指向一个新的子类对象
	p->num_1 = 12;
	p->num_2 = 10;
	cout << p->num_1 << "*" << p->num_2 << "=" << p->getresult() << endl;
}

int main()
{
	test01();
	return 0;
}

一般父类中的虚函数都没什么作用,主要都是调用子类中重写的虚函数,所以可以直接把父类中的虚函数写成纯虚函数,语法:virtual 返回值类型 函数名 (参数列表) =0;

当类中有了纯虚函数,该类也称为纯虚函数,纯虚函数无法实例化对象(无论是堆区还是栈区),子类若不重写纯虚函数则也为纯虚函数。

例2:

#include<iostream>
using namespace std;
//利用多态输出一下网购的流程。 买、取快递、使用
class shopping
{
public:
	virtual void buy() = 0;
	virtual void take() = 0;
	virtual void use() = 0;
	void want_to_eat()
	{
		buy();
		take();
		use();
	}
};

class water :public shopping
{
public:
	void buy()
	{
		cout << "购买农夫山泉。" << endl;
	}
	void take()
	{
		cout << "下楼去取快递。" << endl;
	}
	void use()
	{
		cout << "开盖直接喝水。" << endl;
	}
};
class ice_cream :public shopping 
{
public:
	void buy()
	{
		cout << "网上购买雪糕。" << endl;
	}
	void take()
	{
		cout << "下楼去取快递。" << endl;
	}
	void use()
	{
		cout << "放冰箱里冷冻,想吃拿出来吃" << endl;
	}
};
void test01(shopping * abc)
{
	abc->want_to_eat();
	cout << "-----------------" << endl;
};

int main()
{
	test01(new water);
	test01(new ice_cream);
	return 0;
}

2、类的构造函数跟析构函数

一般构造函数跟类同名,析构函数也同名,不过前要加~

例:

class cat
{
Public:
 Cat()
{
Cout<<”构造函数调用<<endl;
}
~ cat ()
{
Cout<<”析构函数调用<<endl;
}
};

3、虚析构:

(引入虚析构是因为,先父类构造->子类构造,然后就跳过了子类析构,直接到父类析构了,造成了数据泄露)

用来解决父类指针释放子类对象时释放不干净的问题(子类中没有堆区的数据,则可以不写)

虚析构和纯虚析构的共性:

可以解决父类指针释放子类对象,且都需要具体的函数实现

虚析构和纯虚析构的区别:

如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构语法:

Virtual ~ 类名(){}

纯虚析构语法:

Virtual ~ 类名()=0;

类名::~类名(){};//需要写具体实现

五、模板

模板:将类形参化。

模板案例练习:

#include<iostream>
using namespace std;
//利用函数模板来实现数组元素升序。小的在前.
template<typename T>
void myswap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}
//满足选择排序算法的就交换,但是数组元素交换时,因为数据类型不一样,所以要再用一次模板。
template<typename T>
void mysort(T arr[], int len)
{
	//选择排序:
	for (int i = 0; i < len; i++)
	{
		int min = i;
		for (int j = i + 1; j < len; j++)
		{
			if (arr[min] > arr[j])
			{
				min = j;//更新最小值下标。
			}
		}
		if (min != i)
		{
			myswap(arr[min], arr[i]);
		}
	}
	//输出数组。
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

void test01()//字符数组排序
{
	char chararr[] = "badcef";
	int len = sizeof(chararr) / sizeof(char)-1;
	mysort(chararr, len);
}
void test02()//整型数组排序
{
	int intarr[] = { 1,4,3,2,5,6,8,7,9 };
	int len = sizeof(intarr) / sizeof(int);
	mysort(intarr, len);
}
int main()
{
	test01();

	test02();

	system("pause");
	return 0;
}

  普通函数与函数模板的区别:(一般用模板就不用普通函数)
1、如果函数模板和普通函数都可以实现,优先调用普通函数
2、可以通过空模板参数列表来强制调用函数模板:函数名<>(形参);
3、函数模板也可以发生重载
4、如果函数模板可以产生更好的匹配,优先调用函数模板。例:普通函数2个形参都为int型,函数模板为T类型,如果传入char类型的实参,尽管普通函数可以将char类型强制转换成int类型,但是编译器会直接将T推导成char,省去强制转换的步骤,调用函数模板。

利用具体化的模板,可以解决自定义类型的通用化:
template<> 数据类型 函数名 (自定义类型 &a, 自定义类型 &b)

六、深拷贝与浅拷贝

  浅拷贝:简单的赋值拷贝操作,将数据直接复制过来,地址也看做普通数据
深拷贝:在堆区重新申请空间,进行拷贝操作
浅拷贝在遇到堆区开辟,则会delete 2次指针,即同一块内存空间释放2次,非法操作,这时要用深拷贝:(自己写拷贝构造函数)person(const & p){ 其他数据正常等号赋值;  指针的话要  m_height = new int(*p.m_height); } 
也即遇到类的成员有指针时,拷贝构造都要进行深拷贝。
总结:如果属性有在堆区开辟的。一定要自己提供(深)拷贝构造函数防止浅拷贝带来的问题。

七、运算符重载:

语法: 数据类型 operator需要重载的符号 (形参){}
例:person operator+ (person & p1){person temp;   temp.num1=this->num1+p1.num1;  return temp;}
实现运算符加号可以自定义数据类型的数据相加,如果想让返回值作为左值,加&:person & operator+(){}

函数的返回值想要作为左值进行操作,那么在函数声明时:数据类型 & 函数名 (){}

八、补充笔记:

vs里面使用不安全的函数修改“newc++file.cpp”这个文件,将define的内容输入进去,下次在其他.c文件打开的时候自动将该define补充进去。

比如#define _CRT_SECURE_NO_WARNINGS 1可以解决scan函数不安全的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值