参考:https://www.cnblogs.com/feng-sc/p/5710724.html#title11
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
auto AddTest(int a, int b)
{
return a + b;
}
int main()
{
auto index = 10;
auto str = "abc";
auto ret = AddTest(1,2);
std::cout << "index:" << index << std::endl;
std::cout << "str:" << str << std::endl;
std::cout << "res:" << ret << std::endl;
}
auto作为函数返回值时,只能用于定义函数,不能用于声明函数。但如果把实现写在头文件中,可以编译通过,因为编译器可以根据函数实现的返回值确定auto的真实类型。如果读者用过inline类成员函数,这个应该很容易明白,此特性与inline类成员函数类似。
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class Test
{
public:
void TestWork(int index)
{
std::cout << "TestWork 1" << std::endl;
}
void TestWork(int * index)
{
std::cout << "TestWork 2" << std::endl;
}
};
int main()
{
Test test;
test.TestWork(NULL);
test.TestWork(nullptr);
}
NULL在c++里表示空指针,我们调用test.TestWork(NULL),其实期望是调用的是void TestWork(int * index),但结果调用了void TestWork(int index)。但使用nullptr的时候,我们能调用到正确的函数。
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
int main()
{
int numbers[] = { 1,2,3,4,5 };
std::cout << "numbers:" << std::endl;
for (auto number : numbers)
{
std::cout << number << std::endl;
}
}
2.1、std::array
std::array跟数组并没有太大区别,对于多维数据使用std::arraystd::array相对于数组,增加了迭代器等函数
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include
int main()
{
std::array<int, 4> arrayDemo = { 1,2,3,4 };
std::cout << "arrayDemo:" << std::endl;
for (auto itor : arrayDemo)
{
std::cout << itor << std::endl;
}
int arrayDemoSize = sizeof(arrayDemo);
std::cout << "arrayDemo size:" << arrayDemoSize << std::endl;
return 0;
}
2.2、std::forward_list
std::forward_list为从++新增的线性表,与list区别在于它是单向链表。我们在学习数据结构的时候都知道,链表在对数据进行插入和删除是比顺序存储的线性表有优势,因此在插入和删除操作频繁的应用场景中,使用list和forward_list比使用array、vector和deque效率要高很多。
2.3、std::unordered_map
std::unordered_map与std::map用法基本差不多,但STL在内部实现上有很大不同,std::map使用的数据结构为二叉树,而std::unordered_map内部是哈希表的实现方式,哈希map理论上查找效率为O(1)。但在存储效率上,哈希map需要增加哈希表的内存开销。
2.4、std::unordered_set
std::unordered_set的数据存储结构也是哈希表的方式结构,除此之外,std::unordered_set在插入时不会自动排序,这都是std::set表现不同的地方。
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include
#include
#include
#include
int main()
{
std::unordered_set<int> unorder_set;
unorder_set.insert(7);
unorder_set.insert(5);
unorder_set.insert(3);
unorder_set.insert(4);
unorder_set.insert(6);
std::cout << "unorder_set:" << std::endl;
for (auto itor : unorder_set)
{
std::cout << itor << std::endl;
}
std::set<int> set;
set.insert(7);
set.insert(5);
set.insert(3);
set.insert(4);
set.insert(6);
std::cout << "set:" << std::endl;
for (auto itor : set)
{
std::cout << itor << std::endl;
}
}
在C++11以前,C++的多线程编程均需依赖系统或第三方接口实现,一定程度上影响了代码的移植性。C++11中,引入了boost库中的多线程部分内容,形成C++标准,形成标准后的boost多线程编程部分接口基本没有变化,这样方便了以前使用boost接口开发的使用者切换使用C++标准接口,把容易把boost接口升级为C++接口。
1.std::thread
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include
void threadfun1()
{
std::cout << "threadfun1 - 1\r\n" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "threadfun1 - 2" << std::endl;
}
void threadfun2(int iParam, std::string sParam)
{
std::cout << "threadfun2 - 1" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "threadfun2 - 2" << std::endl;
}
int main()
{
std::thread t1(threadfun1);
std::thread t2(threadfun2, 10, "abc");
t1.join();
std::cout << "join" << std::endl;
t2.detach();
std::cout << "detach" << std::endl;
}
有以上输出结果可以得知,t1.join()会等待t1线程退出后才继续往下执行,t2.detach()将线程对象和线程解耦合,主线不等子线程结束,主函数退出,threadfun2还未执行完成,但是在主线程退出后,t2的线程也被已经被强退出。
std::atomic
从功能上看,简单地说,原子数据类型不会发生数据竞争,能直接用在多线程中而不必我们用户对其进行添加互斥资源锁的类型。从实现上,大家可以理解为这些原子类型内部自己加了锁。使用10个线程,把std::atomic_int类型的变量iCount从100减到1。
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
#include
#include
#include
std::atomic_bool bIsReady = false;
std::atomic_int iCount = 100;
void threadfun1()
{
if (!bIsReady) {
std::this_thread::yield();
}
while (iCount > 0)
{
printf("iCount:%d\r\n", iCount--);
}
}
int main()
{
std::atomic_bool b;
std::list<std::thread> lstThread;
for (int i = 0; i < 10; ++i)
{
lstThread.push_back(std::thread(threadfun1));
}
for (auto& th : lstThread)
{
th.join();
}
}
5.1、std::function、std::bind封装可执行对象
test.h
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class Test
{
public:
void Add(std::function<int(int, int)> fun, int a, int b)
{
int sum = fun(a, b);
std::cout << "sum:" << sum << std::endl;
}
};
test.c
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
int add(int a,int b)
{
std::cout << "add" << std::endl;
return a + b;
}
class TestAdd
{
public:
int Add(int a,int b)
{
std::cout << "TestAdd::Add" << std::endl;
return a + b;
}
};
int main()
{
Test test;
test.Add(add, 1, 2);
TestAdd testAdd;
test.Add(std::bind(&TestAdd::Add, testAdd, std::placeholders::_1, std::placeholders::_2), 1, 2);
return 0;
}
5.2、lamda表达式
//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
int main()
{
auto add= [](int a, int b)->int{
return a + b;
};
int ret = add(1,2);
std::cout << "ret:" << ret << std::endl;
return 0;
}
https://blog.csdn.net/pecywang/article/details/22595641
参考:https://blog.csdn.net/u013719339/article/details/80615217
STL将通用容器分为了三类:顺序性容器、关联式容器、容器适配器。
顺序容器的各元素组成有顺序关系的线性表,它是一种线性结构的可序群集。其每个元素有各自固定的位置,使用添加或插入元素后位置顺移,位置与元素本身无关,而与操作时间和地点有关,它保存的是元素操作时的逻辑顺序。例如一次性追加多个元素,这些元素在容器中的相对位置与追加时的逻辑顺序是一致的,即与添加到容器的顺序一致。
vector:向量
1)可以直接访问任何元素。
2)线性顺序结构。可以指定一块连续的空间,也可以不预先指定大小,空间可自动扩展,也可以像数组一样被操作,即支持[]操作符和vector. at(),因此可看做动态数组,通常体现在追加数据push_back()和删除末尾数据pop_back()。
3)当分配空间不够时,vector会申请一块更大的内存块(以2的倍数增长),然后将原来的数据拷贝到新内存块中并将原内存块中的对象销毁,最后释放原来的内存空间。因此如果vector保存的数据量很大时会很消耗性能,因此在预先知道它大小时性能最优。
4)节省空间。因为它是连续存储,在存储数据的区域是没有浪费的,但实际上大多数时候是存不满的,因此实际上未存储的区域是浪费的。
5)在内部进行插入和删除的操作效率低。由于vector内部按顺序表结构设计,因此这样的操作基本上是被禁止的,它被设计成只能在后端进行追加和删除操作。
list:双链表
1)线性链表结构。
2)其数据由若干个节点构成,每个节点包括一个信息块(即实际存储的数据)、一个前驱指针和一个后驱指针。无需分配指定的内存大小且可任意伸缩,因此它存储在非连续的内存空间中,并且由指针将有序的元素链接起来。因而相比vector它也占更多的内存。
3)根据其结构可知随机检索的性能很差,vector是直接找到元素的地址,而它需要从头开始按顺序依次查找,因此检索靠后的元素时非常耗时。即不支持[ ]操作符和. at()。
4)由于list每个节点保存着它在链表中的位置,插入或删除一个元素仅对最多三个元素有所影响,因此它可以迅速在任何节点进行插入和删除操作。
deque:双端队列
1)是一种优化了的、对序列两端进行添加和删除操作、较快速地随机访问的基本序列容器。
2)采用多个连续的存储块保存对象,并在一个映射结构中保存对这些块及其顺序的跟踪。由于不需要重新分配空间,因此追加元素时比vector更有效。实际上内部有一个map指针。
3)支持随机访问,即支持[ ]操作符和. at(),但性能不如vector。
4)可以进行内部随机插入和删除,但性能不如list。
关联容器是二叉树结构,它根据元素特点排序,迭代器能以元素的特点“顺序地”获取元素。它是以键值的方式来保存数据,即把关键字和值关联起来保存,而顺序容器只能保存一种(可以认为它只保存关键字,也可以认为它只保存值)。
集合set:
1)快速查找,不允许重复值。
2)按一定顺序排列,集合中的每个元素被称作集合中的实例。
3)内部通过链表的方式组织,因此插入的时候比vector快,但在查找和末尾追加比vector慢。
map:
1)提供一种“键-值”关系的一对一的数据存储能力。键按一定顺序排列且不可重复(set也可以看成没有键只有值的特殊map形式)。
2)链表方式存储,继承了链表的优缺点。
3)一对多映射,基于关键字快速查找。
**multiset和multimap:**不要求元素唯一,其他同上。
关联容器特点:
1)红黑树的结构原理。
2)set和map保证了元素的唯一性,mulset和mulmap扩展了这一属性,可以允许元素不唯一。
3)元素是有序的集合,默认在插入的时候按升序排列。
4)插入和删除操作比vector快,比list慢。因为vector是顺序存储,而关联容器是链式存储;而同为链式结构,list是线性,而关联容器是排序的二叉树结构,因此每次都需要对元素重新排序,涉及到的元素变动更多。
5)对元素的检索操作比vector慢,比list快很多。vector是顺序的连续存储,这是最快的速度;而list需要逐个搜索,搜索时间与容器大小成正比,关联容器查找的复杂度log(n),因此容器越大,关联容器相对list越能体现其优越性。
6)在使用上set区别于顺序容器的是查询上虽然慢于vector,但却强于list。
7)在使用上map的功能是不可取代的,它保存了“键-值”关系的数据,而这种键值关系采用了类数组的方式。数组是用数字类型的下标来索引元素的位置,而map是用字符型关键字来索引元素的位置。在使用上map也提供了一种类数组操作的方式,即它可以通过下标来检索数据。在STL中只有vector和map可以通过类数组的方式操作元素。
这是一个比较抽象的概念,C++的解释是:适配器是使一事物的行为类似于另一事物的行为的一种机制。容器适配器是让一种已经存在的容器类型采用另一种不同的抽象类型的工作方式来实现的一种机制。其实仅仅是发生了接口转换。可以将之理解为容器的容器,只是不依赖于具体的标准容器类型,可以理解为容器的模板,也可以将之理解为容器的接口。因此,适配器本身不能保存元素,而是“它保存一个容器,容器再保存元素”,所以它是通过调用另一种容器去实现保存功能。STL中提供的三种适配器可以由一种顺序容器去实现,默认下stack和queue基于deque容器实现,priority_queue基于vector容器实现,也可以在创建时可以自己指定具体的实现容器。但由于适配器的特点,并不是任何顺序容器都可以实现这些适配器。
栈stack后进先出。关联容器可以是任意一种顺序容器。 stack的成员函数都是针对其顶部元素进行操作:push(),pop(),top()。
队列queue先进先出。关联容器必须提供pop_front操作,因此vector不适用。它有两个出口。queue也是以deque作为底部结构,封闭其底端的出口和前端的入口。queue,只有顶端(两端)的元素能被外部使用,所以queue也没有迭代器,不提供遍历功能。
优先级priority_queue:最高优先级元素总是第一个处理。则需要提供随机访问功能,因此list不适用。
1.构造函数的命名必须和类名完全相同;而一般方法则不能和类名相同.
2.构造函数的功能主要用于在类的对象创建时定义初始化的状态.它没有返回值,也不能用void来修饰.这就保证了它不仅什么也不用自动返回,而且根本不能有任何选择.而其他方法都有返回值.即使是void返回值,尽管方法体本身不会自动返回什么,但仍然可以让它返回一些东西,而这些东西可能是不安全的.
3.构造函数不能被直接调用,必须通过new运算符在创建对象时才会自动调用,一般方法在程序执行到它的时候被调用.
参考:https://blog.csdn.net/monk1992/article/details/81703737
引入inline函数的主要原因是用它替代C中复杂易错不易维护的宏函数。可以将内联理解为C++中对于函数专有的宏,对于C的函数宏的一种改进。对于常量宏,C++提供const替代;而对于函数宏,C++提供的方案则是inline。C++ 通过内联机制,既具备宏代码的效率,又增加了安全性,还可以自由操作类的数据成员。
例子:
//求0-9的平方
inline int inlineFunc(int num)
{
if(num>9||num<0)
return -1;
return num*num;
}
int main(int argc,char* argv[])
{
int a=8;
int res=inlineFunc(a);
cout<<"res:"<<res<<endl;
}
使用inline函数相当于
int main(int argc,char* argv[])
{
int a=8;
{
int _temp_b=8;
int _temp;
if (_temp_q >9||_temp_q<0) _temp = -1;
else _temp =_temp*_temp;
b = _temp;
}
}
优点:
1.内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。
2.内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。
3.在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能。
4.内联函数在运行时可调试,而宏定义不可以。
缺点:
1.代码膨胀,占用内存。
2.inline函数无法随着函数库升级而升级。
如果f是函数库中的一个inline函数,使用它的用户会将f函数实体编译到他们的程序中。一旦函数库实现者改变f,所有用到f的程序都必须重新编译。如果f是non-inline的,用户程序只需重新连接即可。如果函数库采用的是动态连接,那这一升级的f函数可以不知不觉的被程序使用。
3.是否内联,程序员不可控。
inline函数只是对编译器的建议,是否对函数内联,决定权在于编译器。编译器认为调用某函数的开销相对该函数本身的开销而言微不足道或者不足以为之承担代码膨胀的后果则没必要内联该函数,若函数出现递归,有些编译器则不支持将其内联。
注意:
当在头文件中定义内联函数,那么被多个源文件包含时,如果编译器因为inline函数不适合被内联时,拒绝将inline函数进行内联处理,那么多个源文件在编译生成目标文件后都将各自保留一份inline函数的实体,这个时候程序在连接阶段就会出现重定义错误。解决办法是在需要inline的函数使用static
1.求和思路:
public int findLost(int[] a,int n){
int sum = 0;
int sum1 = 0;
for(int i=0;i<n;i++){
sum+=a[i];
sum1+=i;
}
int res = sum1+n-sum;
return res;
}
2.用异或的方法。任何数异或自己都等于0,任何数异或0都等于他自己。异或两次即可
public int findLost(int[] a,int n){
int result = 0;
for (int i = 0; i < a.length; i++) {
result = result ^ a[i];
}
for (int i = 0; i < n; i++) {
result=result^(i+1);
}
return result;
}
3.用n去分别取减1,2,…,n,看是否为0
首先我们看怎么样的括号字符串是合法的呢?很容易观察到规律,就是从前到后扫描,右括号的数永远不大于左括号的数,到最后左括号的数和右括号的数是相等的。要考虑输出n对所有的合法的括号字符串,那我们可以用分裂的思路,一个字符串往后加:
如果左括号的数
#include
#include
void print_legal_brackets(int n, std::string stack = "", int left = 0, int right = 0) {
if (left == n && right == n) {
std::cout << stack << std::endl;
return;
}
if (left < n) {
print_legal_brackets(n, stack + "(", left + 1, right);
}
if (right < left) {
print_legal_brackets(n, stack + ")", left, right + 1);
}
}
int main(int argc, char *argv[]) {
int n = 0;
while (std::cin >> n) {
print_legal_brackets(n);
}
return 0;
}