亦冥亦魔

吾乃修魔
无情无泪
跟风作对
与天挣命
下得九幽
上可三清
怒断黄泉
笑斩万邪
踏破苍天
卧劈神佛

癫瑕代替了我那不流在南山路上,也不留在雷锋塔下的泪。希望我永远都是你心内的阳光!

风中冥

 

风中冥

老僧呆闺中
心游百里槐
闲庭坐风里
盘膝九曲愁

注:虽每日与天地挣命,但也期有两三闲时,可以悠然南山下。

——————————脩于公元二零一九年三月二十五日

C++ 11 C++14 C++17新功能

一、C++11的新功能:

1.使用nullptr代表空指针:

主要是为了解决C++中NULL带来的二义性问题(因为NULL的值实际是0)

2.自动类型推断关键字auto和decltype()得到类型

①使用auto进行自动类型推导:

使用auto进行类型的自动推导可以大大简化编程工作;auto是在编译期执行,所以不会影响程序运行速度;由于C++编译的特性是从右向左推断类型,所以不会影响编译速度。

//auto的使用示例
//auto tmp; // 错误,auto是通过初始化表达式进行类型推导,如果没有初始化表达式,就无法确定tmp的类型  
auto tmp = 1;//等价于int tmp = 1;  
auto tmp = 1.0; //等价于 double tmp = 1.0;
auto tmp = "Hello World";  //等价于 char *tmp = "Hello World";
auto tmp  = 'A';  //等价于 char tmp  = 'A';
auto func = less<int>();  
vector<int> arr;  
auto it = arr.begin();  //等价于vector<int>::iterator it   = arr.begin(); 
auto p = new foo(); // 对自定义类型进行类型推导

另外auto可以应用于模版:

//auto应用于模版
        //1.不使用auto
	template <class Student, class Teacher>
	void levelUp(const Teacher& th)
	{
		Student * addSalary = th.levelPlus();
		// do somthing with addSalary
	}

	//2.使用auto
	template <class Teacher>
	void levelUp(const Teacher& th)
	{
		auto * addSalary = th.levelPlus();
		// do somthing with addSalary
	}

②使用decltype()得到类型:

//使用decltype()得到变量类型
int x = 3;  
decltype(x) y = x; 

template <class Teacher>
void levelUp(const Teacher& th)->decltype(th.levelPlus();)
{
	auto * addSalary = th.levelPlus();
	// do somthing with addSalary
}

3.使用默认构造函数和删除(default:A()=default; A()=delete;)

为了避免手动编写空默认构造函数,C++11引入了显示默认构造函数的概念,从而仅仅需在类的定义中编写空默认构造函数而不须要在实现文件里提供事实上现,相同的,C++还支持显式删除构造函数的概念。

//默认构造函数和删除
启用默认构造函数A()=default;
删除默认构造函数A()=delete;

4.匿名函数lambda表达式

lambda表达式类似Javascript中的闭包,它可以用于创建并定义匿名的函数对象,以简化编程工作。

Lambda语法:[函数对象参数](操作符重载函数参数)->返回值类型{函数体}

//lambda表达式
vector<int> arr{5, 4, 3, 2, 1};  
int a = 2, b = 1;  


for_each(arr.begin(), arr.end(), [b](int &x){cout<<(x + b)<<endl;});
//b就是指函数可以得到在Lambda表达式外的全局变量
for_each(arr.begin(), arr.end(), [=](int &x){x *= (a + b);});    
 //在[]中传入=的话,即是可以取得所有的外部变量
for_each(arr.begin(), arr.end(), [=](int &x)->int{return x * (a + b);});

//①[]内的参数指的是Lambda表达式可以取得的全局变量
//②()内的参数是每次调用函数时传入的参数
//③->后接的是Lambda表达式返回值的类型

5.智能指针:

shared_ptr、unique_ptr、weak_ptr、auto_ptr,主要为了解决内存泄露问题

详见:http://x.wolfmark.org/x-select_2018-09-10_481.html

(中第23②)

6.右值引用T&&与移动构造函数:

a.函数参数为什么要使用常量引用:

因为函数在参数传递(实参—>形参)的时候会有两种形式:值传递和引用传递。若使用值传递,在遇到大的对象作为参数时会耗费大量时间与内存空间。采用引用就不会有这个拷贝过程。所以使用引用。而我们一般又希望作为参数的引用不要改变原来的对象的内容,所以采用常量引用(const &)。

 b.拷贝构造函数为什么使用常量引用:

拷贝构造函数,首先是一个函数,其次它的任务就是借用一个本类型的对象,拷贝创建一个新的对象。
所以有如下的过程:实参—>形参—>新创建的对象
其中,形参—>新创建的对象,是拷贝构造函数这个函数所赋予的功能(新创建的对象要和作为参数的对象内容相同但是拥有独立的内存空间)。
类的构造函数是没有返回值的,所以实参—>形参之后,还是需要一个形参—>拷贝初始化新对象的过程。

 c.移动构造函数为什么使用右值引用:

首先还是为了不拷贝大对象,所以使用引用。

但是由于引用对象是右值(如字面值或者临时对象),因为这类值本身就是用于临时存储,所以窃取其值不影响程序正确性。

所以这样我们如果确实需要一个单独的传入的参数也不需要拷贝了,就直接将这个传入的参数“据为己有”即可。

总结:移动构造函数就是实参—>形参,形参—>新创建的对象,这两个过程的拷贝都省了。

//(1)声明一个右值引用
int&& i = 42;
或者
class X
{
private:
int* data;
public:X():data(new int[1000000]){}~X(){delete [] data;}
X(const X& other):data(new int[1000000]){std::copy(other.data,other.data+1000000,data);}
//拷贝构造函数
X(X&& other):data(other.data){other.data=nullptr;}};
//移动构造函数

//(2)使用在拷贝构造函数
X x1;
X x2 = std::move(x1);
X x3 = static_cast<X&&>(x2);

//(3)使用在函数模板
//填入左值,推断为左值引用;填入右值,推断为普通无修饰类型。
foo(42);foo(3.14159);foo(std::string());
//填入右值,推断为普通无修饰类型情况
int i=42;foo(i);
//填入左值,推断为左值引用情况

7.初始化列表(更加优雅的初始化方法):

//初始化列表(更加优雅的初始化方法)

//引入C++11之前,只有数组能使用初始化列表,
//其他容器想要使用初始化列表,只能用以下方法:
int arr[3] = {1, 2, 3}  
vector<int> arr(arr, arr + 3); 

//C++11中,我们可以使用以下语法来进行替换:
int arr[3]{1, 2, 3};  
vector<int> arr{1, 2, 3};  
map<int, string>{{1, "a"}, {2, "b"}};  
string str{"Hello World"};

8.加长参数模板:

//加长参数模板

//在C语言中printf可以传入多个参数,在C++11中,
//可以用加长参数模板实现更简洁的Print

template<typename head, typename... tail>  
void Print(Head head, typename... tail) {  
    cout<< head <<endl;  
    Print(tail...);  
}

9.元组tuple:

//C++中的pair可以使用make_pair构造,构造一个包含两种不同类型的数据的容器
auto p = make_pair(1, "C++ 11"); 

//C++11中引入了变长参数模板(参见8),所以发明了新的数据类型:tuple
//tuple是一个N元组,可以传入1个, 2个甚至多个不同类型的数据
auto t1 = make_tuple(1, 2.0, "C++ 11");  
auto t2 = make_tuple(1, 2.0, "C++ 11", {1, 0, 2}); 

10.序列for循环:

//C++中for循环可以使用类似java的简化的for循环,
//可以用于遍历数组,容器,string以及由begin和end函数定义的序列
map<string, int> m{{"a", 1}, {"b", 2}, {"c", 3}};  
for (auto p : m){  
    cout<<p.first<<" : "<<p.second<<endl;  
}

c++11部分参考了文章:https://www.cnblogs.com/guxuanqing/p/6707824.html

二、C++14加入的新功能:

1.Lambda函数在C++11的基础上更加方便:

//C++11要求Lambda参数使用具体的类型声明
auto lambda = [](int x, int y) {return x + y;};

//C++14的泛型Lambda使编写如下语句成为可能:
auto lambda = [](auto x, auto y) {return x + y;};

//新标准中的std::move函数可用于捕获Lambda表达式中的变量,
//这是通过移动对象而非复制或引用对象实现的:
std::unique_ptr ptr(new int(10));
auto lambda = [value = std::move(ptr)] {return *value;};

2.constexpr

在C++11中,使用constexpr声明的函数可以在编译时执行,生成一个值,用在需要常量表达式的地方,比如作为初始化模板的整形参数。C++11的constexpr函数只能包含一个表达式,C++14放松了这些限制,支持诸如if 和switch等条件语句,支持循环,其中包括基于区间(range)的for 循环。

3.类型推导:

C++11仅支持Lambda函数的类型推导,C++14对其加以扩展,支持所有函数的返回类型推导:
auto DeducedReturnTypeFunction();
因为C++14是强类型语言,有些限制需要考虑:

①如果一个函数的实现中有多个返回语句,这些语句一定要推导出同样的类型。

②返回类型推导可以用在前向声明中,但是在使用它们之前,翻译单元中必须能够得到函数定义。

③返回类型推导可以用在递归函数中,但是递归调用必须以至少一个返回语句作为先导,以便编译器推导出返回类型。

C++14带来的另一个类型推导方面的改进是decltype(auto)语法,它支持使用与auto同样的机制计算给定表达式的类型。auto和 decltype在C++11中就已经出现了,但是它们在推导类型时使用了不同的机制,这可能会产生不同的结果。
C++14中的其他改变包括可以声明变量模板,支持使用0b或0B前缀来声明二进制字面常量。InfoQ已经介绍过C++14中可能破坏C++11程序的其他小型修改。
主流C++编译器对新语言特性的支持正在有条不紊地开发:Clang“完全实现了当前草案的所有内容”;GCC和Visual Studio也对C++14的新特性提供了一些支持。

三、C++17加入的新功能:

1.使 static_assert 的文本信息可选

2.删除 trigraphs

3.在模板参数中允许使用 typename(作为替代类)

4.来自 braced-init-list 的新规则用于自动推导

5.嵌套命名空间的定义,例如:使用 namespace X::Y { … } 代替 namespace X { namespace Y { … }}

6.允许命名空间和枚举器的属性

7.新的标准属性:[[fallthrough]], [[maybe_unused]] 和 [[nodiscard]]

8.UTF-8 字符文字

9.对所有非类型模板参数进行常量评估

10.Fold 表达式,用于可变的模板

11.A compile-time static if with the form if constexpr(expression)

12.结构化的绑定声明,现在允许 auto [a, b] = getTwoReturnValues();

13.if 和 switch 语句中的初始化器

14.在某些情况下,确保通过编译器进行 copy elision(Guaranteed copy elision by compilers in some cases)

15. 一些用于对齐内存分配的扩展

16.构造函数的模板推导,允许使用 std::pair(5.0, false) 代替std::pair<double,bool>(5.0, false)

17.内联变量,允许在头文件中定义变量

18.__has_include,允许由预处理程序指令检查头文件的可用性

19.__cplusplus 的值更改为 201703L

 

 

猿类面试小书(C++,游戏,unity方向,后台)

除此之外还有两个讲基础知识的博客推荐给大家:

1.https://github.com/CyC2018/CS-Notes

2.https://github.com/linw7/Skill-Tree

3.一个linux内核相关的项目TKeed:

https://github.com/linw7/TKeed

 

C++面试常考基础(C++常考基础知识)

(由修根据深信服等公司的流出面试题和各公司的面经总结)

本文地址:http://x.wolfmark.org/x-select_2018-09-10_481.html

1.C++基本类型和占用的存储空间:

①整型:char(1B)、char16_t(2B)、char32_t(4B)、short(2B)、int(2B/4B)、long(4B)、long long (8B)、指针(2B\4B);32\64位系统int和指针占用内存大小不同、int至少和short一样长,long至少32位(4B)、且至少和int一样长;long long 至少64位(8B)、且至少和long一样长。

②浮点型:float(4B)、double(8B)、long double(16B)

数值范围:

类型 比特数 有效数字 数值范围
float 32 6~7 -3.4*10(-38)~3.4*10(38)
double 64 15~16 -1.7*10(-308)~1.7*10(308)
long double 128 18~19 -1.2*10(-4932)~1.2*10(4932)

 

2.C++中的拷贝构造函数用形参用值传递会有什么影响(网易游戏、尼禄)?

当一个函数需要以值 的方式传递时,编译器会生成代码调用它的拷贝构造函数以生成一个副本;所以一个类的拷贝构造函数以值的方式传递参数时,需要以值的方式传递一个(这个类的)对象作为实参,从而需要调用类的拷贝构造函数;结果是调用类的拷贝构造函数导致又一次调用拷贝构造函数,导致无限递归。

 

3.C++中new和C中的malloc的区别(尼禄、zoom):

①属性:

new/delete是C++关键字,需要编译器的支持,malloc/free是库函数,需要头文件stdlib.h的支持。

②参数:

使用new操作符申请内存分配时无需制定内存块的大小,编译器会根据类型信息自行计算;而malloc则需要显示的指出所需的内存尺寸。

③返回类型:

new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是复合类型安全的操作符。而malloc内存分配成功则是返回void*,需要通过强制类型转换将void*指针转换成我们需要的类型。例如:
buffer=(char*)malloc(i+1);

④分配失败:

new内存分配失败时,会跑出bad_malloc异常。malloc分配内存失败时返回NULL。

⑤自定义类型:

new会先调用operator new 函数,申请足够的内存(通常在底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型的指针。delete先调用析构函数,然后调用opertor delete函数来释放内存(通常底层用free实现)。
malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。

⑥重载:

C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是制定了一个地址作为内存的起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回地址。而malloc不允许重载。

⑦内存区域:

new操作符从自由存储区(freestore)上为对象动态分配内存,而malloc函数从堆上动态分配内存。

 

4.C++中用父类指针指向了子类实例,用父类指针调用了虚函数,调用的是子类的函数还是父类的函数?

父类指针指向子类实例对象,调用普通重写函数时,会调用父类中的函数。而调用被子类重写虚函数时,会调用子类中的函数。
再次说明了,子类中被重写的虚函数的运行方式是动态绑定的,与当前指向类实例的父类指针类型无关,仅和类实例对象本身有关。

 

5.C++中哪些机制可以替代宏定义常量:

①const替换宏

有两个需要注意的地方:
(1) 当定义的是常量指针,需要const两次( const char* const iVar = “C++”),第一个const是防止值被改变,第二个const防止地址被改变。
(2) 当定义到class内时,需要用static来修饰这个变量,防止出现多个实体。

② enum代替#define

 

6.C++在new失败后的处理方法:

①C++标准做法

new失败会抛出bad_alloc异常,用try catch来捕获

try
{
    double *ptr=new double[1000000];
}
catch(bad_alloc &memExp)
{
    //失败以后,要么abort要么重分配
    cerr<<memExp.what()<<endl;
}

② 分配内存的时候创建一个指针,判断是否是空指针

double *ptr=new double[1000000];
if( 0 == ptr)

 

7.某函数希望既可以被C调用,又可以被C++调用,应该怎么声明它的原型?

①C++调用C:

C++语言支持函数重载,C 语言不支持函数重载。函数被C++编译后在库中的名字与C 语言的不同。假设某个函数的原型为: void foo(int x, int y);该函数被C 编译器编译后在库中的名字为_foo , 而C++ 编译器则会产生像_foo_int_int 之类的名字。
C++提供了C 连接交换指定符号extern“C”来解决名字匹配问题。

/*TestC.h*/
#ifndef TESTC_H
#define TESTC_H

#ifdef __cplusplus
extern "C"{
#endif
int add(int a , int b);

#ifdef __cplusplus
}
#endif

#endif

 

/*TestC.c*/
#include"TestC.h"

int add(int a , int b)
{
  return(a+b);
}
/*TestC.cc*/
#include <iostream>
#include "TestC.h"
int main()
{
  cout << add(2,5) << endl ;
  return 0;
}

②C调用C++函数:

用一个函数将C++类的使用封装起来,然后将它外部声明为C函数就可以了。

/* ADD.h */
#ifdef ADD_H
#define ADD_H
class ADD
{
public:
	int add(int a, int b)
	{
		return(a+b);
	}
};
#endif // ADD_H

//*将C++类封装为C函数文件
/*ADD.cc*/
#include "ADD.h"

extern "C" int add_cpp(int a , int b);

int add(int a , int b)
{
	ADD tmp ;
	return tmp.add(a,b);
}

//*实际调用C++代码的C文件
/* add.c */

extern int add_cpp(int a , int b);

int main()
{
	cout << add_cpp(2,3) << endl;
	return 0;
}

 

8.编写C++静态成员函数需要注意哪些注意事项,通常有什么作用:

注意事项:

①静态成员函数只能访问类的静态数据成员,不能访问非静态数据成员;
②静态成员函数不要滥用,否则背离了面向对象编程的初衷;
③类的非静态成员函数可以调用静态成员函数,但是反之则不行。
作用:

C++提供了静态成员,用以解决同一个类的不同对象之间数据成员和函数的共享问题。
静态成员的特点是:不管这个类创建多少个对象,其静态成员在内存中只保留一份副本,这个副本为该类的所有对象所共享。

 

9.举例说明explicit关键字的用法:

C++中的explicit关键字只能用于修饰构造函数, 它的作用是表明该构造函数是显式的, 而非隐式的, 跟它相对应的另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为implicit(隐式)。explicit关键字的作用就是防止类构造函数的隐式自动转换。

//A使用隐式转换implicit
class A
{
public:
	A(int){}
	A(int,int){}
	operator bool const {return true;}
};
//B类型使用显式转换explicit
class B
{
public:
	explicit B(int){}
	explicit B(int,int){}
	explicit operator bool const {return true;}
};


int main()
{
	A a1 = 1 ; //OK,使用A::A(int)
	A a2(2); //OK,使用A::A(int)
	A a3{4,5};//OK,使用A::A(int,int);
	A a4 ={4,5};//OK,使用A::A(int,int);
	A a5 =  (A)1;//OK,使用 static_cast
	bool na1 = a1 ;//OK,复制构造函数使用 A::operator bool()
	bool na2 = static_cast<bool>(a1);//OK  使用static_cast 直接赋值


	B b1 = 1 ; //出错,不会使用B::B(int)
	B b2(2); //OK,使用B::B(int)
	B b3{4,5};//OK,使用B::B(int,int);
	B b4 ={4,5};//出错,不会使用B::B(int,int);
	B b5 =  (B)1;//OK,使用 stbtic_cbst
	bool nb1 = b1 ;//出错,不会使用 B::operbtor bool()
	bool nb2 = stbtic_cbst<bool>(b1);//OK  使用stbtic_cbst 直接赋值
}

 

10.c++的普通成员函数能否作为线程的线程函数,为什么?假设原型都是void fun(void *):

一般来说,C++的类成员函数不能作为线程函数。这是因为在类中定义的成员函数,编译器会给其加上this指针。当把线程函数封装在类中,this指针会作为默认的参数被传进函数中,从而和线程函数参数(void*)不能匹配,不能通过编译。
解决方法:
①将该成员函数声明为static类型,去掉this指针;
②不定义类成员函数为线程函数,而将线程函数定义为类的友元函数。

 

11.如果父类的析枸函数不是virtual的,会有什么影响?

如果父类的析构函数不是virtual,在释放子类对象的内存时不会调用子类的析构函数而会直接调用父类的析构函数,导致内存泄露。

 

12.const 成员函数: const int fun() const ;两个const 分别表示什么意思:

①函数的返回值类型是const。 这个const修饰没什么意义,因为函数的返回值必须是固定值。
②int fun() const{} 则是类的常成员函数。它不能更新对象的数据成员,也不能调用该类中没有const修饰的成员函数(这就保证了在常成员函数中绝对不会更改数据成员的值)。
③如果将一个对象说明为常对象,则通过该常对象只能调用它的常成员函数,而不能调用其他成员函数。

 

13.写出重载前++和后++的函数声明:

①前++:

self & operator++();

②后++:

self operator++(T);//T是类型名,如int

(只能自增一次,实际上在内部调用了“前++”)

 

14.纯虚函数和虚函数的区别:

①虚函数:
C++的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现。
子类可以重写父类的虚函数实现子类的特殊化。
②纯虚函数:
C++中的纯虚函数也是一种“运行时多态”。
C++中包含纯虚函数的类,被称为是“抽象类”。抽象类不能使用new出对象,只有实现了这个纯虚函数的子类才能new出对象。
C++中的纯虚函数更像是“只提供申明,没有实现”,是对子类的约束,是“接口继承”。

 

15.C++中禁止类被派生的方法:

将构造函数声明为私有(private)或者保护的(protected),就会阻止 类被实例化。

 

16.C++中如何禁止子类默认的拷贝函数,默认构造拷贝函数不会被执行?

class T{
    T(const T&) = delete; //禁用复制构造函数
    T(T&&) = default;   //启用默认的移动构造函数
};

 

17.C++全局对象的和构造顺序可控么?

C++规定,在同一个编译单元(同一个.cpp程序)中,先定义的对象先创建,后定义的对象后创建;而销毁时则相反,后定义的对象先销毁,先定义的对象后销毁。
可以用一个类来保证初始化顺序(详见Thinking in C++)。

 

18.c++基类的构造函数或者析构函数中调用的是被派生的类覆盖的虚函数么?

在子类对象的基类子对象构造期间,调用的虚函数的版本是基类的而不是子类的。因为基类构造器是在派生类之前执行的,所以在基类构造器运行的时候派生类的数据成员还没有被初始化。如果在基类的构造过程中对虚函数的调用传递到了派生类, 派生类对象当然可以参照引用局部的数据成员,但是这些数据成员其时尚未被初始化。
析构期间也是一样的逻辑:一旦一个派生类的析构器运行起来,该对象的派生类数据成员就被假设为是未定义的值,这样以来,C++就把它们当做是不存在一样。一旦进入到基类的析构器中,该对象即变为一个基类对象,C++中各个部分(虚函数,dynamic_cast运算符等等)都这样处理。

 

19.虚函数和纯虚函数以及重载:

纯虚函数由子类实现,虚函数是默认实现,允许基类指针来调用子类的函数,普通函数是强制实现,子类不应该重写。重载函数在类型和参数数量上一定不同,重定义要求参数类型和个数、函数返回值类型相同;虚函数必须是类的成员函数;构造函数可以重载,但不能是虚函数,析构函数可以是虚函数。

 

20.C++中与类型转换相关的四个关键字和它们的特点:

static_cast
特点:静态转换,在编译处理期间。
应用场合:主要用于C++中内置的基本数据类型之间的转换,但是没有运行时类型的检测来保证转换的安全性。
用于基类和子类之间的指针或引用之间的转换,这种转换把子类的指针或引用转换为基类表示是安全的;进行下行转换,把基类的指针或引用转换为子类表示时,由于没有进行动态类型检测,所以是不安全的。把void类型的指针转换成目标类型的指针(不安全)。
不能用于两个不相关的类型转换。
不能把const对象转换成非const对象。

const_cast
特点:去常转换,编译时执行。
应用场合:const_cast操作不能在不同的种类间转换。相反,它仅仅把它作用的表达式转换成常量。它可以使一个本来不是const类型的数据转换成const类型的,或者把const属性去掉。

reinterpret_cast
特点: 重解释类型转换
应用场合:它有着和c风格强制类型转换同样的功能;它可以转化任何的内置数据类型为其他的类型,同时它也可以把任何类型的指针转化为其他的类型;它的机理是对二进制进行重新的解释,不会改变原来的格式。

 

dynamic_cast
特点:可以将基类指针或引用安全的转化为派生类的指针或者引用。
应用场合:dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

 

21.C++中struct和class的区别,C#?

1.)C++:
最本质的一个区别就是默认的访问控制,体现在两个方面:
② 默认的继承访问权限。struct是public的,class是private的。
②struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的。
2.)C#:
class(类)是面向对象编程的基本概念,是一种自定义数据结构类型,通常包含字段、属性、方法、属性、构造函数、索引器、操作符等。在.NET中,所有的类都最终继承自System.Object类,因此是一种引用类型,也就是说,new一个类的实例时,在堆栈(stack)上存放该实例在托管堆(managed heap)中的地址,而实例的值保存在托管堆(managed heap)中。
struct(结构)是一种值类型,用于将一组相关的变量组织为一个单一的变量实体 。所有的结构都继承自System.ValueType类,因此是一种值类型,也就是说,struct实例在创建时分配在线程的堆栈(stack)上,它本身存储了值。所以在使用struct时,我们可以将其当作int、char这样的基本类型类对待。

22.C++什么时候会调用析构函数?

析构函数在下边3种情况时被调用:
①对象生命周期结束,被销毁时(析构函数的调用顺序与构造函数的调用顺序相反);
②delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时;
③对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。

 

23.C++解决内存泄漏的方法:

①养成配套使用new和delete的习惯
②使用智能指针(智能指针会自动删除分配的内存):
a. shared_ptr:
shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。在最后一个shared_ptr析构的时候,内存才会被释放。
b. unique_ptr
a) unique_ptr是一个独占的智能指针,他不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另外一个 unique_ptr;
b) unique_ptr不允许复制,但可以通过函数返回给其他unique_ptr,还可以通过std::move来转移到其他的unique_ptr,这样它本身就不再拥有原来指针的所有权了。
c) 如果希望只有一个智能指针管理资源或管理数组就用unique_ptr,如果希望多个智能指针管理同一个资源就用shared_ptr。
c. weak_ptr
弱引用的智能指针weak_ptr是用来监视shared_ptr的,不会使引用计数加一,它不管理shared_ptr内部的指针,主要是为了监视shared_ptr的生命 周期,更像是shared_ptr的一个助手。 weak_ptr没有重载运算符*和->,因为它不共享指针,不能操作资源,主要是为了通过shared_ptr获得资源的监测权,它的构造不会增加引用计数,它的析构不会减少引用计数,纯粹只是作为一个旁观者来监视shared_ptr中关连的资源是否存在。 weak_ptr还可以用来返回this指针和解决循环引用的问题。

 

24.C++引用和指针的区别(zoom面试):
①引用必须在创建时初始化,而指针不需要;
②引用一旦绑定了一个对象,则不能改变为对另一个对象的引用,而指针可以随时改变;
③引用时必须有一块合法的内存单元,不能为nullptr引用。
④需要为指针分配内存空间,而不必为引用;
⑤可以有多级指针,但是没有多级引用;
⑥用指针传递参数,可以实现对实参的改变;引用传递的是实参本身,不会创建临时副本,对形参的修改实际上是修改实参。

 

25.C++单例模式:

通过单例模式可以保证类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
单例模式的要点有三个:a. 单例类只能有一个实例 b. 它必须自行创建这个实例 c. 它必须自行向整个系统提供提供这个实例
优点:减少了时间和空间的开销,提高了封装性。
具体实现:将构造函数、复制构造函数、析构函数和重载赋值号=等设为私有,禁止重载;提供一个静态的函数接口以供外部调用。

class SingleSample
{
public:
	static SingleSample * getInterface();
private:
	SingleSample();
	SingleSample(const SingleSample &);
	SingleSample & operator=(const SingleSample &);
	~SingleSample();

	static SingleSample * interface ;
}

//初始化:
SingleSample * SingleSample::interface;
SingleSample * SingleSample::getInterface()
{
	return interface;
}

 

26.C++ 11 C++14 C++17 带来的新功能(尼禄面试):

详细内容:http://x.wolfmark.org/x-select_2018-09-29_497.html

(1)C++11:

①使用nullptr代表空指针
②自动类型推断(关键字auto和decltype()得到类型)
③使用默认构造函数和删除(default:A()=default; A()=delete;)
④匿名函数lambda表达式
⑤智能指针(shared_ptr、unique_ptr、weak_ptr、auto_ptr)
⑥右值引用T&&与移动构造函数
⑦初始化列表(更加优雅的初始化方法)
⑧加长参数模板
⑨tuple元组
⑩序列化for循环

(2)C++14:

①Lambda函数在C++11的基础上更加方便
②constexpr
③类型推导的拓展

(3)C++17:

①使 static_assert 的文本信息可选
②删除 trigraphs
③在模板参数中允许使用 typename(作为替代类)
④来自 braced-init-list 的新规则用于自动推导
⑤嵌套命名空间的定义,例如:使用 namespace X::Y { … } 代替 namespace X { namespace Y { … }}
⑥允许命名空间和枚举器的属性
⑦新的标准属性:[[fallthrough]], [[maybe_unused]] 和 [[nodiscard]]
⑧UTF-8 字符文字
⑨对所有非类型模板参数进行常量评估
⑩Fold 表达式,用于可变的模板
⑪A compile-time static if with the form if constexpr(expression)
⑫结构化的绑定声明,现在允许 auto [a, b] = getTwoReturnValues();
⑬if 和 switch 语句中的初始化器
⑭在某些情况下,确保通过编译器进行 copy elision(Guaranteed copy elision by compilers in some cases)
⑮一些用于对齐内存分配的扩展
⑯构造函数的模板推导,允许使用 std::pair(5.0, false) 代替std::pair<double,bool>(5.0, false)
⑰内联变量,允许在头文件中定义变量
⑱__has_include,允许由预处理程序指令检查头文件的可用性
⑲__cplusplus 的值更改为 201703L

 

27.strlen()和sizeof()的区别(zoom面试):

(1)定义不同:

sizeof()是运算符,在头文件中typedef为unsigned int,其值在编译时即计算好了,参数可以是数组、指针、类型、对象、函数等。
strlen()是函数,要在运行时才能计算。参数必须是字符串指针类型。当数组名作为参数传入时,实际上数组就退化为指针了。

(2)功能不同:

sizeof():获得能保证容纳实现所建立的最大对象的字节大小。

由于在编译时计算,因此sizeof不能用来返回动态分配的内存空间的大小。实际上,用sizeof来返回类型以及静态分配的对象、结构或数组所占的空间,返回值跟对象、结构、数组所存储的内容没有关系。
具体而言,当参数分别如下时,sizeof返回的值表示的含义如下:
数组——编译时分配的数组空间大小;
指针——存储该指针所用的空间大小(存储该指针的地址的长度,是长整型,应该为4);
类型——该类型所占的空间大小;
对象——对象的实际占用空间大小;
函数——函数的返回类型所占的空间大小。函数的返回类型不能是void。

strlen():返回字符串的长度。字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字串的的第一个地址开始遍历,直到遇到结束符NULL。返回的长度大小不包括NULL。

(3)举例:

	char s[] ="hello";
	char *p = s;
        //注意 \0 不算在内
	cout << strlen(s) << endl;//5
	cout << strlen(p) << endl;//5
	cout << sizeof(s) << endl;//6
	cout << sizeof(p) << endl;//4

28.C++继承中重载、重写、重定义的区别(zoom面试):

(1)成员函数重载的特征:

①在相同的范围内(同一个类中)重载
②函数名称相同
③函数参数不同
④virtual关键字可有可无

(2)重写(覆盖)是指派生类函数覆盖基类函数的特征:

①不同的范围,分别位于派生类和基类中
②函数名称相同
③函数参数相同
④基类函数中必须有virtual关键字

(3)重定义(隐藏)是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

①如果派生类的函数和基类的函数同名,但参数不同,此时不管有没有virtual,基类的函数被隐藏
②如果派生类的函数与基类的函数同名,但参数也相同,基类函数没有virtual关键字,基类的函数被隐藏

 

 本文地址:

(由修根据深信服等公司的流出面试题和各公司的面经总结)

更多秋招知识请看-秋招总结-猿类面试小书:

惊破天

惊破天

心绪踟躇莫扶摇

条框摇摆断前路

来生浮尘碎虚梦

铁骑横飞踏破天

 

注:大田赛归来,没有取得理想的成绩,却也在意料之中,更让我确信了长跑是一个需要时间来沉淀的运动。我总认为摇摆不定的心境是阻碍我稳步向前的最大障碍,凡世的条条框框只会让我断了向前的希望;人的梦想,如果不去做,难道要等到来生?化为灰烬,唯有磨炼己身至一副钢筋铁骨才能踏破这天空。

——————————脩于公元二零一八年七月二十二日夜

windows 下配置mysql-5.7.21(最新免安装版)

由于以前一直在linux服务端上使用mysql,今天在windows笔记本上装了下,顺便写下了过程:
(注:由于软件时境过迁,只保证2018.4.10日(今天)此方法可用,后续软件升级后可能也会有些许出入)
一、在Mysql官网下载Mmysql-5.7.21的ZIP文件(免安装文件)
下载链接为:https://dev.mysql.com/downloads/mysql/
找到你的系统对应的版本下载,比如这里我的是:mysql-5.7.21-winx64.zip

二、解压ZIP文件
比如解压至:D:/soft/ ,更改 mysql-5.7.21-winx64 文件夹名称为 mysql

三、配置环境变量

1、新建一个变量:MYSQL_HOME

变量值:D:/soft/mysql

2、修改path变量

添加一条记录:%MYSQL_HOME%/bin

四、在D:/soft/mysql目录下创建如下my.ini文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[mysqld]

#绑定IPv4
bind-address = 0.0.0.0

# 设置mysql的安装目录,即你解压缩安装包的位置
basedir = D:/soft/mysql

# 设置mysql数据库的数据的存放目录
datadir = D:/soft/mysql/data

# 设置端口号
port = 3306

# 允许最大连接数
max_connections = 200

# 设置字符集为utf8
loose-default-character-set = utf8

# 开启查询缓存
explicit_defaults_for_timestamp = true

# windows下区分表名大小写(0:不区分,2区分(5.7版本))

lower_case_table_names=2

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

[client]
#设置客户端字符集
port=3306
default-character-set = utf8

[WinMySQLadmin]
Server = D:/soft/mysql/bin/mysqld.exe
skip-grant-tables

五、将mysql注册为Windows系统服务

1、使用管路员权限打开CMD(注意管理员权限),在CMD中进入mysql的解压目录

2、运行服务安装命令

mysqld install MySQL –defaults-file=”%MYSQL_HOME%\my.ini”

安装成功后会提示安装成功

备注:如果想要移除该服务,使用命令:mysqld -remove

六、启动mysql服务

1、启动服务命令为:net start mysql
2、此时会提示:
启动不了mysql服务,提示发送系统错误2:”系统找不到指定文件”
①首先打开注册表
win+r打开运行,输入regedit,打开注册表
②更改注册表:找到
HKEY_LOCAL_MACHINE
-SYSTEM
-CurrentControlSet
-services-mysql(服务名)
-ImagePath

更改原来的值(默认目录):”C:\Program Files\MySQL\MySQL Server 5.7\mysqld” –defaults-file=D:\soft\mysql\my.ini MySQL

为(安装目录):”D:\soft\mysql\bin\mysqld” –defaults-file=”D:\soft\mysql\mysql5.6.17\my.ini” mysql

2、打开管理工具 服务,找到MySQL服务。通过右键选择启动或者直接点击左边的启动来启动服务

创建命令:

1
mysqld --initialize-insecure --user=mysql

七、修改root账号密码

刚安装完成时root账号默认密码为空,此时可以将密码修改为指定的密码。

打开数据库:

1
mysql -uroot -p

(提示 Enter password,直接回车,默认密码为空)

1
2
3
4
5
6
7
use mysql;

update mysql.user set authentication_string=password('123456') where user='root'

flush privileges;

quit;

密码修改完成后既可以使用新密码登录。

参考了文章:博客园:mysql-5.7.18 免安装版安装配置(Windows)

南去丶离外婆家

南去丶离外婆家

秋劲冰迎来

春窒雪送去

明朝繁华至

还归外婆家

无题—–题于狄道

边陲小镇夜无声,无车乏人似难行。
脚踩红龙跨宝马,任游长河西湖羞!

闲游西湖群寺(三则)

闲游西湖群寺(三则)

 

三生石上生三枝,

三生石后鸣乱笛。

精魂问我诺千金?

邪冥犹忧到北冥。

素面一碗酒一杯,

江南一载一荣枯。

不知学海怎无涯,

高僧亦从教学来。

灵隐繁香佛乘仙,

千年古刹意浩然。

财神庙前财运俱,

“懒驴”庙外臭堆沙。

——————————脩于公元二零一七年十月七日

昨日,闲来无事,遂去闲游,不知欲至何处。想来来江南已一载有余,还未尝见石,曰:三生。石边冷清,遂取笛而鸣,然声涩难鸣,故名乱笛。三生石中上石恰生三枝,在石下向上望,中石上一怪客,仿若骷髅(也许便是传说中的精魂吧),仿佛可以洞穿人心。午间山下小寺边食一碗素面,恰过清泉,泉边一寺,寺内“教学楼”见所未见,笑叹“和尚也教学”。大寺庙宇香火不断,千年古刹意蕴犹存,不足方圆的三丈财神庙内求签之人之热切满溢。财神庙外却是那九百五十级让烦人望而却步的长路,浑身腥臭的“懒驴”驮着沙从山底运至山顶。