new创建对象时,对象的内存空间是在 栈 中的,其作用范围只是在函数内部,函数执行完成后就会调用析构函数,删除该对象。new创建对象,是创建在堆中的,必须要程序员手动的管理该对象的内存空间。malloc() 函数在 C 语言中就出现了,在 C++ 中仍然存在,但建议尽量不要使用 malloc() 函数。new 与 malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。::进行实现implicit, 意思是隐藏的。类构造函数默认情况下即声明为implicit(隐式)explicit关键字的作用就是防止类构造函数的隐式自动转换explicit关键字依然有效, 此时, 当调用构造函数时只传入一个参数, 等效于只有一个参数的类构造函数在链表中,申请空间时代码如下:
temp1=(struct ListNode*)malloc(sizeof(struct ListNode));由于结构体内存在 next**指针 ,而申请结构体空间后同时定义了 next**指针 ,此时 next**指针** 未指向任何空间,故在测试时可能导致上述错误。
解决方法为:增加代码使 next**指针** 指向空。
temp->next=NULL;C++ 中有两种类型的表达式:
<string.h>是旧的、不在std中的 C/C++ 版本;<string>是头文件的内容在std中的 C++ 版本,对应的是新的string 类;<cstring>是对应于旧的、但在std中的 C 版本。(C库包含在C++标准库中)std中。std 的那部分标准库创建的新头文件名,生成新头文件的方法仅仅是将现有C++头文件名中的.h 去掉**,如:<iostream>、<complex>;C语言头文件标准化后,头文件名前带个c字母,如<cstdio>、<cstring>、<ctime>、<ctype>;std中(在标准化的过程中,库中有些部分的细节被修改了,所以旧头文件和新头文件中的实体不一定完全对应)。std**中。void func() throw ();void func() throw (int, double, A, B, C){...}
局部变量与全局变量同名时,要使用全局变量:::a
#:字符串化的意思,出现在宏定义中的#,是把跟在后面的参数转换成一个字符串。 # 的主要作用是将宏参数不经扩展地转换成字符串常量。"或\时,它们前面会自动被加上转义字符 \。#define MKSTR( x ) #x
cout << MKSTR(HELLO C++) << endl;
转换成了:
cout << "HELLO C++" << endl;##: 连接符号,把参数连在一起。将多个 Token 连接成一个 Token。要点: #define CONCAT( x, y ) x ## y
int xy = 100;
cout << concat(x, y);
转换成了:
cout << xy;std::thread是C++11接口;pthread是C++98接口,且只支持Linux。std::thread对比于pthread的优缺点:
优点:
缺点:
struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。struct和class关键字在C++中其基本语法是完全一样的。
C语言编程单位是函数,语句是程序的基本单元。而C++语言的编程单位是类。从c到c++的设计由以过程设计为中心向以数据组织为中心转移。
在C++中struct得到了很大的扩充:
容器 | 对应的迭代器类型 |
|---|---|
array | 随机访问迭代器 |
vector | 随机访问迭代器 |
deque | 随机访问迭代器 |
list | 双向迭代器 |
set / multiset | 双向迭代器 |
map / multimap | 双向迭代器 |
forward_list | 前向迭代器 |
unordered_map / unordered_multimap | 前向迭代器 |
unordered_set / unordered_multiset | 前向迭代器 |
stack | 不支持迭代器 |
queue | 不支持迭代器 |
注意,容器适配器 stack 和 queue 没有迭代器,它们包含有一些成员函数,可以用来对元素进行访问。
迭代器定义方式 | 具体格式 |
|---|---|
正向迭代器 | 容器类名::iterator 迭代器名; |
常量正向迭代器 | 容器类名::const_iterator 迭代器名; |
反向迭代器 | 容器类名::reverse_iterator 迭代器名; |
常量反向迭代器 | 容器类名::const_reverse_iterator 迭代器名; |
- 对**正向**迭代器进行 `++` 操作时,迭代器会指向容器中的**后**一个元素;
- 而**对反**向迭代器进行 `++` 操作时,迭代器会指向容器中的**前**一个元素。C++11右值引用(一看即懂)
可以取地址的,有名字的,非临时的就是左值;
不能取地址的,没有名字的,临时的就是右值;
左值引用要求右边的值必须能够取地址,如果无法取地址,可以用常引用;但使用常引用后,我们只能通过引用来读取数据,无法去修改数据,因为其被const修饰成常量引用了。
在c++中,临时对象不能作为左值,但可以作为常量引用const &。
右值只在当前表达式有效。右值性质:
x = plus(y, z) // plus(y, z)
x = (y+z) // (y+z)右值引用是用来支持转移语义的。转移语义可以将资源 ( 堆,系统对象等 ) 从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 应用程序的性能。临时对象的维护 ( 创建和销毁 ) 对性能有严重影响。
右值引用用来绑定到右值,绑定到右值以后本来会被销毁的右值的生存期会延长至与绑定到它的右值引用的生存期。
通常我们对类成员进行“初始化”有两种方式:
: => 系统创建成员变量时就初始化。初始化这个数据成员的时候函数体还未执行。根据C++的规则,const类型和引用不可以被赋值,只能被初始化。
class A
{
public:
A(int& c) {
_a = 1;
}
protected:
int _a;
// 报错,成员_b和_c必须在构造函数的成员初始化列表里面初始化
const int _b;
int& _c;
};应改为,在构造函数后面通过冒号,使用括号表达式进行初始化:
class A
{
public:
A(int& c): _b(2), _c(c) {
_a = 1;
}
protected:
int _a;
const int _b;
int& _c;
};单冒号**:**
class Base { };
class Derived : public Base { };
class Base {
public:
int a=10;
};
class Derived : public Base {
};
int main() {
Derived b;
cout<<b.a<<endl;
}class MyClass {
public:
void public_member() { }
private:
void private_member() { }
};class MyClass {
private:
int member;
public:
MyClass() : member(0) { }
};class MyClass {
public:
unsigned first : 1;
unsigned second : 2;
unsigned third : 4;
};双冒号**::**
在某些情况下,初始化的真实含义依赖于传递初始值时用的是花括号 {}还是圆括号 ()。
vector<int> v1(10); // 有10个元素,每个的值都是默认初始化0
vector<int> v2{10}; // 有1个元素,且值是10
vector<int> v3(10, 1); // 有10个元素,每个的值都是1
vector<int> v4{10, 1}; // 有2个元素,分别是10和1如果用的是圆括号,可以说提供的值是用来构造 vector对象的。如果用的是花括号,可以表述成我们想列表初始化该vector对象。
另一方面,如果初始化时使用了花括号的形式,但是提供的值又不能用来列表初始化,就要考虑用这样的值来构造vector对象了。
vector<string> v5{"hi"}; // 列表初始化,有一个元素
vector<string> v6("hi"); // 错误,不能使用字符串字面值构建vector对象
vector<string> v7{10}; // 有10个默认初始化的元素
vector<string> v8{10, "hi"}; // 有10个值为"hi"的元素确认无法执行列表初始化后,编译器会尝试用默认值初始化vector对象。
总结:(a,b)用来说明有a个b值,{a,b,c}表示用列表abc来初始化,当{}中内容跟vector内类型不同时就尝试替换为()的规则。还是不要乱用吧,太混乱了…
一个const成员函数如果以引用的形式返回*this,那么他的返回类型将是常量引用。
// 如果display返回常量引用,则调用set将引发错误
screen.display(cout).set('*');编译器处理完类中的全部声明后,才会处理成员函数的定义。
类型名的定义通常出现在类的开始处,这样就能确保所有使用该类型的成员都出现在类名的定义之后。
如果类的成员是const、引用,或属于某种未提供默认构造函数的类类型,我们必须通过构造函数初始值列表为这些成员提供初值。
类成员的初始化顺序与它们在类定义中的出现顺序一致。最好令构造函数初始值的顺序与成员声明的顺序保持一致。
如果一个构造函数为所有参数都提供了默认实参,则它实际上也定义了默认构造函数。
关键字explicit只对一个实参的构造函数有效。需要多个实参的构造函数不能用于执行隐式转换,所以无须将这些构造函数指定为explicit的。只能在类内声明构造函数时使用explicit关键字,在类外部定义时不应重复。
当在类的外部定义静态成员时,不能重复使用static关键字,该关键字只出现在类内部的声明语句。