指针与引用
C++为什么不允许引用更改指向对象(重新赋值)?
- 因为1那样不过是多了一种新的指针而已。这样做,等于有两种不同的指针。其中一种是多余的。
- 事实上,指针要兼容C 是必须要有的。
- 引用对于C++来说也是必须有的。
- C++引用,有一个确定的对象,因此不会有空的引用。
- 如果允许引用其他对象的话,就会忍不住让它称为空指针一样的东西(空引用?)
- 引用初始化,用的是
=,对引用的对象赋值,也是=。如果允许改变所引用的对象,=是不能够承担这项任务的,于是必须有=以及,另外一个赋值号 ,表示引用绑定对象这个功能。这增加了语言的复杂性,更不好用了。
- 引用编译器也是通过指针实现的,只不过加了一些语言级别的限制,使得引用用起来更方便,也更安全。
- 引用会影响对象生存期。如果引用编译时不能确定引用生存期结束时被引用的对象是谁,问题就会变得很糟糕
已经有指针,为什么需要引用?
从汇编看c++中的引用和指针 | 为什么 C++ 有指针了还要引用? | 知乎-为什么 C++ 有指针了还要引用?
汇编的操作是一样的。
结论:直接的原因是为了支持运算符重载
指针是为了兼容C,在C++提出引用是因为运算符重载。
- 用指针的使用经常犯得错:1,操作空指针,2,操作野指针,3,不知不觉改变了指针的值,而后还以为该指针正常。
- 如果我们要正确的使用指针,我们不得不人为地保证这三个条件。而引用的提出就解决了这个问题。
- 引用区别于指针的特性是 :1,不存在空引用(保证不操作空指针),2,必须初始化(保证不是野指针),3,一个引用永远指向他初始化的那个对象(保证指针值不变)。C++中引用不能再更改绑定的对象。
不需要区分对引用本身的操作和对所引用对象的操作。
因为加入引用是为了支持operator overloading。这里有一个假设,如果没有引用,那么,用指针来operator overloading操作。A operator +(const A *a, const A *_a);那么使用的时候,&a + &b,这样看起来是不是很难受。而引入引用的概念,既可以满足overload operator,也不失重载value和pointer的灵活性。而且引用还带来一个指针无法替代的特性: 引用临时对象。因为引用必须在定义的时候就赋值,以后无法更改。
另外,对于链表的归并排序,48. 排序链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* divide_from(ListNode* sublist)
{
if(!sublist)return NULL;
ListNode *slow,*fast;
slow=sublist;
fast=slow->next;
while(fast)
{
fast=fast->next;
if(fast)
{
slow=slow->next;
fast=fast->next;
}
}
fast=slow->next;
slow->next=NULL;
return fast;
}
ListNode *merge(ListNode *L1, ListNode *L2)
{
ListNode dummy(-1);
ListNode *current=&dummy;
while(L1 && L2)
{
if(L1->val<=L2->val)
{
current->next=L1;
current=L1;
L1=L1->next;
}
else
{
current->next=L2;
current=L2;
L2=L2->next;
}
}
if(L1)current->next=L1;
if(L2)current->next=L2;
return dummy.next;
}
void recursive_merge_sort(ListNode* &head) // 记得head要引用,传递指针的引用,该引用绑定的是指针,不是指针指向的对象
{
if(head && head->next)
{
ListNode* second_half=divide_from(head);
recursive_merge_sort(head);
recursive_merge_sort(second_half);
head=merge(head,second_half);
}
}
ListNode* sortList(ListNode* head) {
recursive_merge_sort(head);
return head;
}
};
个人看法:在参数传递时,如果传递指针,会产生指针的副本,对指针所指向对象的操作,与引用该对象时对引用的操作一致,但是如果要修改指针本身,修改指针的指向关系,需要引用该指针。
强引用和弱引用
指针与数组
char* p[10]; 与 char(*p1)[10];
cout << sizeof(char) << ", " << sizeof(char*) << endl;
cout << sizeof(int) << ", " << sizeof(int*) << endl;
cout << sizeof(double) << ", " << sizeof(double*) << endl;
输出
1, 4
4, 4
8, 4
求以下输出:
#include<iostream>
using namespace std;
int main()
{
char* p[10]; // 指针数组
char(*p1)[10]; // 数组指针
cout << sizeof(p) << ", " << sizeof(p1) << endl;
return 0;
}
输出
40, 4
又
const char* p = "hello world // 需要const
char p1[] = "hello world!"; // 不可以p1++
cout << sizeof(p) << ", " << sizeof(p1) << endl; // 输出4,13(=12+1)
cout << ++p<< endl; // 输出ello world!
数组下标负数
refer to link
在需要通过数组下标来访问数组时,通常建议将下标定义size_t类型,因为一般来说在进行下标访问时,下标都是正的。但是,也不是所有的下标访问操作下标都是正的,比如以下代码:
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* ptr = &(a[4]);
std::cout << ptr[-2] << std::endl;
这段代码中对ptr进行了下标访问,并且下标是负的,但是这个访问并不是错误的,执行的结果是输出3。
数据类型
C++ named requirements: PODType
refer to link
statement
C++ switch case 匹配字符串
refer to link
refer to c++ - Why the switch statement cannot be applied on strings
在c++中,是不能用字符串来作为case的标签的.
size_t 和 size_type的区别
refer to link
为了使自己的程序有很好的移植性,c++程序员应该尽量使用size_t和size_type而不是int, unsigned
size_t是全局定义的类型;size_type是STL类中定义的类型属性,用以保存任意string和vector类对象的长度string::size_type制类型一般就是unsigned int, 但是不同机器环境长度可能不同 win32 和win64上长度差别;size_type一般也是unsigned int- 使用的时候可以参考:
string::size_type a =123;
vector<int>size_type b=234;
size_t b=456;
size_t使用的时候头文件需要<cstddef>;size_type使用的时候需要<string>或者<vector>sizeof(string::size_type),sizeof(vector<bool>::size_type),sizeof(vector<char>::size_type),sizeof(size_t), 上述长度均相等,长度为win32:4 win64:8- 二者联系:在用下标访问元素时,
vector使用vector::size_type作为下标类型,而数组下标的正确类型则是size_t。
STL
std::vector和std::string不支持push_front
refer to link
- 可以通过insert()插入到begin()
- 因为vector的设计是为了O(1)
push_back(),而push_front()的性能相当于O(n)的insert(),所以不提供push_front避免误用。类似vector但能O(1)push_front()的是deque。 - STL接口具有明确的时间复杂度,不提供看似高效但实则低效的操作。
- 同理C++中list和forward_list的迭代器不支持随便加减一个数,不能
iter=iter+1,只能++或者--。因为双向链表和单向链表存储元素都不是在一块连续的内存上。
deque 添加删除元素 迭代器失效问题
C++中_T的含义
_T("Hello")是一个宏,他的作用是让你的程序支持Unicode编码,因为Windows使用两种字符集ANSI和UNICODE,前者就是通常使用的单字节方式,但这种方式处理象中文这样的双字节字符不方便,容易出现半个汉字的情况。而后者是双字节方式,方便处理双字节字符。- Windows NT的所有与字符有关的函数都提供两种方式的版本,而Windows 9x只支持ANSI方式。
如果你编译一个程序为ANSI方式,
_T实际不起任何作用。而如果编译一个程序为UNICODE方式,则编译器会把"Hello"字符串以UNICODE方式保存。 _T和_L的区别在于,_L不管你是以什么方式编译,一律以UNICODE方式保存。- Visual C++里边定义字符串的时候,用
_T来保证兼容性,VC支持ascii和unicode两种字符类型,用_T可以保证从ascii编码类型转换到unicode编码类型的时候,程序不需要修改。 - 如果将来你不打算升级到unicode,那么也不需要
_T,
C++之_In_和_out_
refer to link
- 在阅读代码时,经常会看到函数声明的形参会有_In_和_out_修饰符,_In_和_out_在这里是宏,它们的实际意思是
_In_表明这个变量或参数是输入值,即你必须给这个变量填写好以后提交给某个函数去执行_out_表明这个是输出值,即你可以传个地址给形参,函数内部会往这个地址写地- 这两个宏并不会参与编译,它仅仅是对程序员起到提示作用,让程序员明白如何调用该函数
- 类似的宏还有
__out输出参数__in_opt可选的输入参数_opt参数是可选的,就是可以为NULL_ecount所指向的缓存的元素个素,也就是括号里的数字
有序容器和无序关联容器
refer to link
Ordered v Unordered Associative Containers
- Ordered Associative Container
- Standard Traversal encounters elements in sorted order
- Order predicate may be specified
- Default order predicate is "less than", defined using operator< for the element type
- Popular implementations: OrderedVector, BinarySearchTree
- Search operations required to have O(log n) runtime
- Insert, Remove operations should either be seldom used or have O(log n) runtime
- Unordered Associative Container
- Standard Traversal encounters elements in unspecified order
- Search, Insert, Remove operations should have average-case constant runtime
- Popular implementations use hashing
直接初始化与拷贝初始化
- 直接初始化:要求编译器使用普通的函数匹配来选择与我们提供的参数最匹配的构造函数。
- 拷贝初始化:要求编译器将右侧运算对象拷贝到正在创建的对象中,如果需要的话还要进行类型转换。拷贝初始化通常使用拷贝构造函数(copy constructor)来完成。
- 拷贝构造函数的参数必须是引用类型。因为:拷贝构造函数被用来初始化非引用类类型。如果其参数不是引用类型,则调用永远不会成功——为了调用拷贝构造函数,我们必须拷贝它的实参,为了拷贝实参,我们又需要调用拷贝构造函数。
- explicit构造函数只能用于直接初始化。通过将构造函数声明为explicit抑制构造函数定义的隐式转换。关键字explicit只对一个实参的构造函数有效。
What is a copy constructor? When is it used?
A copy constructor is a constructor which first parameter is a reference to the class type and any additional parameters have default values.
When copy initialization happens and that copy initialization requires either the copy constructor or the move constructor.
- Define variables using an =
- Pass an object as an argument to a parameter of nonreference type
- Return an object from a function that has a nonreference return type
- Brace initialize the elements in an array or the members of an aggregate class
- Some class types also use copy initialization for the objects they allocate.
What is a destructor? What does the synthesized destructor do? When is a destructor synthesized?
The destructor is a member function with the name of the class prefixed by a tilde(~).
As with the copy constructor and the copy-assignment operator, for some classes, the synthesized destructor is defined to disallow objects of the type from being destoryed. Otherwise, the synthesized destructor has an empty function body.
The compiler defines a synthesized destructor for any class that does not define its own destructor.
编译器内置宏
ANSI C标准中有几个标准预定义宏(也是常用的):
__LINE__:在源代码中插入当前源代码行号;
__FILE__:在源文件中插入当前源文件名;
__DATE__:在源文件中插入当前的编译日期
__TIME__:在源文件中插入当前编译时间;
__STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;
__cplusplus:当编写C++程序时该标识符被定义。