下面github中的内容有空看一下
https://github.com/huihut/interview
复习《C++程序设计》笔记
第八章知识点 类和对象
class 访问属性
inline成员函数
函数的调用设计到堆栈的操作,时间开销远远大于顺序执行小规模的程序,如果在class内定义的成员函数中不包含循环等控制结构,C++编译器会把它们当作内置函数(inline)来处理
class成员函数的存储方式
类class定义一个对象object,编译系统会为每一个对象分配存储空间,每一个object的数据成员空间独立的,而所有objcet的成员函数共用存储空间,不同的对象使用的是同一段代码空间,为区分不同对象的数据成员,使用this指针来实现区分
第九章 类和对象进一步
this 指针
每一个成员函数中都包含一个特殊的指针,这个指针叫做this指针,指向本类对象的指针,他的数值是当前被调用的成员函数所在对象的起始地址
this指针是隐式的,它作为参数传递给成员函数的,例如定义类Box的成员函数volume时候
int Box::volume()
{
return (height * width * length);
}
C++ 编译器会把它处理为,在行参列表中增加了一个this指针
int Box::volume(Box *this)
{
return (this->height * this->width * this->length);
}
在调用a.volume()时候,实际是调用的a.volume(&a)
P282,const等等挺不好理解的,现在先不看了
常对象:Time const t1(10, 12, 13)对象中所有的数据成员的值不能被修改,参考https://github.com/huihut/interview中const介绍
// 类
class A
{
private:
const int a; // 常对象成员,只能在初始化列表赋值
public:
// 构造函数
A() : a(0) { };
A(int x) : a(x) { }; // 初始化列表
// const可用于对重载函数的区分
int getValue(); // 普通成员函数
int getValue() const; // 常成员函数,不得修改类中的任何数据成员的值
};
void function()
{
// 对象
A b; // 普通对象,可以调用全部成员函数、更新常成员变量
const A a; // 常对象,只能调用常成员函数
const A *p = &a; // 常指针
const A &q = a; // 常引用
// 指针
char greeting[] = "Hello";
char* p1 = greeting; // 指针变量,指向字符数组变量
const char* p2 = greeting; // 指针变量,指向字符数组常量
char* const p3 = greeting; // 常指针,指向字符数组变量
const char* const p4 = greeting; // 常指针,指向字符数组常量
}
// 函数
void function1(const int Var); // 传递过来的参数在函数内不可变
void function2(const char* Var); // 参数指针所指内容为常量
void function3(char* const Var); // 参数指针为常指针
void function4(const int& Var); // 引用参数在函数内为常量
// 函数返回值
const int function5(); // 返回一个常数
const int* function6(); // 返回一个指向常量的指针变量,使用:const int *p = function6();
int* const function7(); // 返回一个指向变量的常指针,使用:int* const p = function7();
第11 章知识点 继承和派生
11.1 继承方式
11.2 派生类的构造函数和析构函数
执行顺序:派生类构造函数先会调用基类的构造函数,然后再执行派生类自己的构造函数,析构顺序相反
11.3 多层派生时候的构造函数
Student(int n, string nam)
Student1(int n,string nam, int a):Student(n, nam)
Student2(int n,string nam, int a, int s):Student1(n, nam, a)
每一层派生类的构造函数,只需要写上一层派生类的构造函数即可
11.4 多重继承
多重继承会引起二义性的问题:一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,那么在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。
C++提供了虚基类的方法,使得继承间接共同基类的时候只保留一份成员
11.5 虚函数
用同一种方法去调用同一类族中不同类的所有同名函数,可以把基类中的函数前面加上virtual来声明
虚函数的使用方法:
this 指针
this
指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向调用该成员函数的那个对象。this
指针,然后调用成员函数,每次成员函数存取数据成员时,都隐式使用 this
指针。this
指针被隐含地声明为: ClassName *const this
,这意味着不能给 this
指针赋值;在 ClassName
类的 const
成员函数中,this
指针的类型为:const ClassName* const
,这说明不能对 this
指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作);this
并不是一个常规变量,而是个右值,所以不能取得 this
的地址(不能 &this
)。this
指针:list
// 声明1(加 inline,建议使用)
inline int functionName(int first, int second,...);
// 声明2(不加 inline)
int functionName(int first, int second,...);
// 定义
inline int functionName(int first, int second,...) {/****/};
// 类内定义,隐式内联
class A {
int doA() { return 0; } // 隐式内联
}
// 类外定义,需要显式内联
class A {
int doA();
}
inline int A::doA() { return 0; } // 需要显式内联
优点
缺点
volatile int i = 10;
断言,是宏,而非函数。assert 宏的原型定义在 <assert.h>
(C)、<cassert>
(C++)中,其作用是如果它的条件返回错误,则终止程序执行。可以通过定义 NDEBUG
来关闭 assert,但是需要在源代码的开头,include <assert.h>
之前。
assert() 使用
#define NDEBUG // 加上这行,则 assert 不可用
#include <assert.h>
assert( p != NULL ); // assert 不可用
Bit mode: 2; // mode 占 2 位
类可以将其(非静态)数据成员定义为位域(bit-field),在一个位域中含有一定数量的二进制位。当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会用到位域。
extern "C"
修饰的变量和函数是按照 C 语言方式编译和链接的extern "C"
的作用是让 C++ 编译器将 extern "C"
声明的代码当作 C 语言代码处理,可以避免 C++ 因符号修饰导致代码不能和C语言库中的符号进行链接的问题。
extern "C" 使用
#ifdef __cplusplus
extern "C" {
#endif
void *memset(void *, int, size_t);
#ifdef __cplusplus
}
总的来说,struct 更适合看成是一个数据结构的实现体,class 更适合看成是一个对象的实现体。
联合(union)是一种节省空间的特殊的类,一个 union 可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。当某个成员被赋值后其他成员变为未定义状态。联合有如下特点:
union 使用
#include<iostream>
union UnionTest {
UnionTest() : i(10) {};
int i;
double d;
};
static union {
int i;
double d;
};
int main() {
UnionTest u;
union {
int i;
double d;
};
std::cout << u.i << std::endl; // 输出 UnionTest 联合的 10
::i = 20;
std::cout << ::i << std::endl; // 输出全局静态匿名联合的 20
i = 30;
std::cout << i << std::endl; // 输出局部匿名联合的 30
return 0;
}
面向对象程序设计(Object-oriented programming,OOP)是种具有对象概念的程序编程典范,同时也是一种程序开发的抽象方针。
面向对象三大特征 —— 封装、继承、多态
把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。关键字:public, protected, private。不写默认为 private。
public
成员:可以被任意实体访问protected
成员:只允许被子类及本类的成员函数访问private
成员:只允许被本类的成员函数、友元类或友元函数访问
栈 stack:存放函数的参数值、局部变量,由编译器自动分配释放
堆 heap:
堆的生长空间向上,地址越大,栈的生长空间向下,地址越小
构造函数(包括拷贝构造)、析构函数、赋值运算符重载函数
防止内存泄露,delete p(基类)的时候,它很机智的先执行了派生类的析构函数,然后执行了基类的析构函数。
如果基类的析构函数不是虚函数,在delete p(基类)时,调用析构函数时,只会看指针的数据类型,而不会去看赋值的对象,这样就会造成内存泄露
static 静态变量,指类的静态成员函数、静态成员变量是和类相关的,而不是和类的具体对象相关的。即使没有具体对象,也能调用类的静态成员函数和成员变量。一般类的静态函数几乎就是一个全局函数,只不过它的作用域限于包含它的文件中
在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化,如:double Account::Rate = 2.25;static关键字只能用于类定义体内部的声明中,定义时不能标示为static
在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。
const数据成员 只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值是什么。
函数体内: static 修饰的局部变量作用范围为该函数体,不同于auto变量,其内存只被分配一次,因此其值在下次调用的时候维持了上次的值
模块内:static修饰全局变量或全局函数,可以被模块内的所有函数访问,但是不能被模块外的其他函数访问,使用范围限制在声明它的模块内
类中:修饰成员变量,表示该变量属于整个类所有,对类的所有对象只有一份拷贝
类中:修饰成员函数,表示该函数属于整个类所有,不接受this指针,只能访问类中的static成员变量
注意和const的区别!!!const强调值不能被修改,而static强调唯一的拷贝,对所有类的对象
Struct 和Union区别
结构体:将不同类型的数据组合成一个整体,是自定义类型; 共同体:不同类型的几个变量共同占用一段内存
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块
修改内容上的差别:
char a[] = "hello";
a[0] = ‘X‘;
char *p = "world"; // 注意p 指向常量字符串
p[0] = ‘X‘; // 编译器不能发现该错误,运行时错误
用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。
char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12 字节
cout<< sizeof(p) << endl; // 4 字节
//计算数组和指针的内存容量
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4 字节而不是100 字节
}
先申请一定的大小的数组, 当数组填满之后,另外申请一块原数组两倍大的新数组, 然后把原数组的数据拷贝到新数组, 最后释放原数组的大小
STL(Standard Template Library,标准模板库)
从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其他一些组件的集合
STL包括两部分内容:容器和算法。
容器即存放数据的地方,比如array, vector,分为两类,序列式容器和关联式容器
算法有排序,复制等,以及各个容器特定的算法
迭代器是STL的精髓,迭代器提供了一种方法,使得它能够按照顺序访问某个容器所含的各个元素,但无需暴露该容器的内部结构,它将容器和算法分开,让二者独立设计。
vector数据结构
vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。
因此能高效的进行随机存取,时间复杂度为o(1);
但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内存块的拷贝,时间复杂度为o(n)。
另外,当数组中内存空间不够时,会重新申请一块内存空间并进行内存拷贝
list数据结构
list是由双向链表实现的,因此内存空间是不连续的。
只能通过指针访问数据,所以list的随机存取非常没有效率,时间复杂度为o(n);
但由于链表的特点,能高效地进行插入和删除
vector和list的区别
我们看一个简单的vector和list使用示例:
#include<iostream>
#include<vector>
#include<list>
using namespace std;
int main()
{
vector<int> v;
list<int> l;
for(int i=0;i<8;i++) ////往v和l中分别添加元素
{
v.push_back(i);
l.push_back(i);
}
cout<<"v[2]="<<v[2]<<endl;
//cout<<"l[2]="<<l[2]<<endl; //编译错误,list没有重载[]
cout<<(v.begin()<v.end())<<endl;
//cout<<(l.begin()<l.end())<<endl; /编译错误,list::iterator没有重载<或>
cout<<*(v.begin()+1)<<endl;
//cout<<*(l.begin()+1)<<endl; //编译错误,list::iterator没有重载+
vector<int>::iterator itv=v.begin();
list<int>::iterator itl=l.begin();
itv = itv+2;
//itl=itl+2; //编译错误,list::iterator没有重载+
itl++; //list::iterator中重载了++,只能使用++进行迭代访问。
itl++;
cout<<*itv<<endl;
cout<<*itl<<endl;
getchar();
return 0;
}
vector拥有一段连续的内存空间,能很好的支持随机存取,
因此vector<int>::iterator支持“+”,“+=”,“<”等操作符。
list的内存空间可以是不连续,它不支持随机访问,
因此list<int>::iterator则不支持“+”、“+=”、“<”等
vector<int>::iterator和list<int>::iterator都重载了“++”运算符。
总之,如果需要高效的随机存取,而不在乎插入和删除的效率,使用vector;
如果需要大量的插入和删除,而不关心随机存取,则应使用list
1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。
进程间通信方式:
线程间通信方式:
原文:https://www.cnblogs.com/skydaddy/p/11605981.html