C++学习笔记(2)
C++学习笔记(2)
思维导图
浅拷贝和深拷贝:
- 浅拷贝:简单的赋值拷贝;
- 深拷贝:在堆区重新申请空间,进行拷贝;
- 浅拷贝的问题:堆区内存重复释放;
- 可以利用深拷贝解决这个问题;
深拷贝构造函数
//深拷贝的测试
class Person2
{
public :
int mAge;
int* mHeight;
Person2()
{
mAge = 0;
mHeight = 0;
cout << "无参构造的调用" << endl;
}
Person2(int age,int height)
{
cout << "有参构造的调用" << endl;
mAge = age;
mHeight = new int(height);
}
Person2(const Person2 & p)
{
cout << "深拷贝构造的调用" << endl;
this->mAge = p.mAge;
//this->mHeight = p.mHeight;//默认拷贝构造的代码,执行 浅拷贝
this->mHeight = new int(*p.mHeight);//自定义深拷贝
}
~Person2()
{
cout << "析构函数的调用" << endl;
if (mHeight != NULL)delete mHeight;//释放堆内存
mHeight = NULL;//将指针成员变量置空,防止野指针
}
};
void test12()
{
Person2 p1(22,150);
cout << "p1.mAge=" << p1.mAge << " *p1.mHeight=" << *p1.mHeight << " (int)p1.mHeight=" << (int)p1.mHeight << endl;
Person2 p2(p1);
cout << "p2.mAge=" << p2.mAge << " *p2.mHeight=" << *p2.mHeight << " (int)p2.mHeight=" << (int)p2.mHeight << endl;
}
int main()
{
test12();
system("pause");
return 1;
}
运行结果
拷贝构造使用时机:
- 使用一个已经创建好的对象创建一个新对象;Person p2(p1);
- 值传递的方式给函数参数传值;void doWork(Person p){};
- 以值的方式返回局部对象;Person func(){Person p;return p;}
以值方式传递参数和返回对象,都会创建一个副本,也就是拷贝构造;
//构造函数 析构函数
class Person
{
public:
int age;
Person() { this->age = 0; };//无参构造
Person(int age) { this->age = age; };//有参构造
Person(const Person& p) { this->age = p.age; };//拷贝构造
~Person() {};//析构函数
};
//值传递的方式传参
void doWork(Person p4)
{
cout << "(int)&p4=" << (int)&p4 << " p4.age=" << p4.age << endl;
}
//值方式返回一个对象
Person doWork()
{
Person p5 = 55;
cout << "(int)&p5=" << (int)&p5 << " p5.age=" << p5.age << endl;
return p5;
}
//拷贝构造调用时机
void test11()
{
//从一个已有的对象创建一个新的对象
Person p1=11;
Person p2(p1);
cout << "(int)&p1=" << (int)&p1 << " p1.age=" << p1.age << endl;
cout << "(int)&p2=" << (int)&p2 << " p2.age=" << p2.age << endl;
//值传递的方式,将对象作为参数进行传递
Person p3 = 33;
cout << "(int)&p3=" << (int)&p3 << " p3.age=" << p3.age << endl;
doWork(p3);
//值传递的方式,返回一个对象
Person p6 = doWork();
cout << "(int)&p6=" << (int)&p6 << " p6.age=" << p6.age << endl;
}
运行结果
三类构造函数:无参构造、有参构造、拷贝构造;无参构造和有参构造统称为普通构造;
三类调用方式:括号法,显示法,隐式转换法;
//构造函数
class Person
{
public:
int age;
Person() { this->age = 0; };//无参构造
Person(int age) { this->age = age; };//有参构造
Person(const Person& p) { this->age = p.age; };//拷贝构造
};
void test10()
{
//一、括号法
Person p1;
Person p2(10);
Person p3(p2);
//二、显示法
Person p4 = Person();
Person p5 = Person(20);
Person p6 = Person(p5);
//三、隐式转换法
Person p7 = 30;
Person p8 = p7;
cout << "p1.age=" << p1.age << endl;
cout << "p2.age=" << p2.age << endl;
cout << "p3.age=" << p3.age << endl;
cout << "p4.age=" << p4.age << endl;
cout << "p5.age=" << p5.age << endl;
cout << "p6.age=" << p6.age << endl;
cout << "p7.age=" << p7.age << endl;
cout << "p8.age=" << p8.age << endl;
}
运行结果
仿函数(函数对象)测试
仿函数的本质并不是函数,而是重载了operator()的类;
1、仿函数可以像普通函数那样进行调用,可以有参数和返回值;
2、仿函数超越普通函数的概念,可以拥有自己的状态(属性)
3、仿函数可以作为函数的参数进行传递
//仿函数(函数对象)测试
//仿函数的本质并不是函数,而是重载了operator()的类;
//1、仿函数可以像普通函数那样进行调用,可以有参数和返回值;
//2、仿函数超越普通函数的概念,可以拥有自己的状态(属性)
//3、仿函数可以作为函数的参数进行传递
//有参数和返回值的仿函数
class Add
{
public:
int operator()(int a, int b)const
{
return a + b;
}
};
//有状态(属性)的仿函数
class MyPrint
{
public:
int count;
MyPrint()
{
this->count = 0;
}
void operator()(string s)
{
cout << s << endl;
count++;
}
};
//作为参数传递的仿函数
void doPrint(MyPrint &print,string s)
{
print(s);
}
void test9()
{
Add add;
cout<<"有参数和返回值的仿函数add(5, 8)=" << add(5, 8) << endl<<endl;
MyPrint print;
for (size_t i = 0; i < 5; i++)
{
print("我是有状态的 仿函数!");
cout << "仿函数调用的次数=" << print.count << endl;
}
cout << endl;
string s = "仿函数作为参数进行传递!";
doPrint(print,s);
}
运行结果
C++的8种容器类的要点总结
置物之所也;
数组、链表、树、栈、队列、结合、映射表;
序列式容器:强调值的顺序,每个元素都有固定的位置;
关联式容器:二叉树,元素之间没有固定的物理顺序;
一、string字符串类compare,substr,insert,assign,=,append,+=,at,[ ],
二、vector单端数组push_back,pop_back,insert,size,capacity,resize,empty
,erase,clear,at,[ ],front,back,swap交换,收缩容量vector<int>(v).swap(v);,
reserve预留空间,
三、deque双端数组/中控器,push_front,pop_front,push_back,pop_back,
insert,begin,end,front,back,=,assign,empty,size,resize,insert,erase,clear,
at,[ ],sort,
四、stack栈容器,只有一个出入口,先进后出,栈底,栈顶top,入栈push,出栈pop,empty,size,=,不允许遍历,
五、queue队列,先进先出,一端为出口,一端为入口,不允许遍历,push,pop,front,back,empty,size,
六、list链表,内存不连续,由一系列节点组成(数据域+指针域),双向循环链表,
push_front,pop_front,push_back,pop_back,front,back,begin,end,prev,next,
assign,=,swap,empty,size,resize,inset,erase,clear,remove,
不支持(at,[ ]),不支持随机访问,reverse,sort,
- 可以快速对任意位置插入/删除元素,而无需移动其他元素;
- 动态分配内存,不会造成内存浪费和溢出;
- 空间(指针域)时间(遍历)的额外耗费比较大;
- 插入和删除操作不会造成原迭代器的失效,这在vector中不成立;
七、set集合multiset,属于关联式结构,二叉树,所有元素插入时自动排序,
set不允许有重复元素,multiset允许重复元素;
empty,size,swap,insert,erase,clear,find(返回迭代器或end),count(重复元素的个数),set插入会返回插入结果(成功或失败=重复或不重复),,
不支持(头插,头删,尾插,尾删,resize),sort,
仿函数=函数重载/运算符重载;
默认排序从小到大,可以利用仿函数改变排序规则(重载小括号())
pair对组,默认构造,make_pair,,,,,
八、map容器,所有元素都是pair,第一个元素称为key,第二个元素称为value;
所有元素都会按照键值自动排序,本质是一个关联式容器,底层实现是二叉树;
优点:快速按照key查找元素,map不允许重复key,multimap允许重复key;
默认构造、拷贝构造、=,empty,size,swap,insert,erase,clear,
find按照key查找,返回迭代器或者end,count统计key的个数;
默认排序从小到大,可以利用仿函数改变排序规则(重载小括号())
利用仿函数重新定义排序规则
//利用仿函数重新定义排序规则
#include<set>
//输出set容器内容,重载一
void printSet(const set<int>& s)
{
//遍历set容器并输出
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << " " << *it;
}
cout << endl;
}
//仿函数,重新定义set集合的排序规则(从大到小)
class MyCompare
{
public:
bool operator ()(int a, int b)const
{
return a > b;
}
};
//输出set容器内容,重载二
void printSet(const set<int, MyCompare>& s)
{
//遍历set容器并输出
for (set<int, MyCompare>::iterator it = s.begin(); it != s.end(); it++)
{
cout << " " << *it;
}
cout << endl;
}
void test8()
{
//定义一个从小到大排序的set容器
set<int> set1;
for (size_t i = 0; i < 10; i++)
{
set1.insert(i);
}
printSet(set1);
//使用仿函数,定义一个从大到小排序的set容器
set<int, MyCompare>set2;
set2.insert(1);
for (size_t i = 0; i < 10; i++)
{
set2.insert(i);
}
printSet(set2);
}
运行结果
vector的四种遍历方法测试
//迭代器测试iterator
void myPrint(int a)
{
cout <<&a<<" = " << a << endl;
}
void test6()
{
vector<int> v;
v.push_back(11);
v.push_back(22);
v.push_back(33);
v.push_back(44);
v.push_back(55);
//方法一
cout << "---------方法一----------------" << endl;
vector<int>::iterator begin = v.begin();
vector<int>::iterator end = v.end();
while (begin!=end)
{
cout <<"begin._Ptr="<<begin._Ptr<<" *begin=" << *begin << endl;
begin++;
}
cout << "---------方法二----------------" << endl;
//方法二
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << "it._Ptr=" << it._Ptr << " *it=" << *it << endl;
}
cout << "-----------方法三--------------" << endl;
//方法三
for (size_t i = 0; i < v.size(); i++)
{
cout << "&v[i]=" << &v[i] << " v[i]=" << v[i] << endl;
}
cout << "---------方法四----------------" << endl;
//方法四
for_each(v.begin(), v.end(), myPrint);
}
运行结果
vector测试
//vector测试
void test7()
{
vector<int>v;
for (size_t i = 0; i < 100; i++)
{
cout << "v.size()=" << v.size() << " v.capacity()=" << v.capacity() << " sizeof(v)=" << sizeof(v) << endl;
v.push_back(1);
}
}
运行结果
迭代器
void test6()
{
vector<int> v;
v.push_back(11);
v.push_back(22);
v.push_back(33);
v.push_back(44);
v.push_back(55);
vector<int>::iterator begin = v.begin();
vector<int>::iterator end = v.end();
while (begin!=end)
{
cout <<"begin._Ptr="<<begin._Ptr<<" *begin=" << *begin << endl;
begin++;
}
}
运行结果