194-C++重要知识点9

1.没有私有友元和公有友元这么一说

2.5种可访问属性:①私有②公有③保护④默认情况下私有⑤友元

3.数组也是一个简单的容器,容器是能够容纳其他对象的对象

4.下面程序会打印出数组ar的所有元素的值

int main()
{
	int ar[] = [1,2,3,4,5,6,7,8,9];
	for(auto x:ar)
	{
		cout << x << endl;
	}
}

auto的意思就是根据变量推演出数据类型

每次都会创建一个副本x,对x的改变并不会改变数组ar中的值,所以一般我们都会写成引用的方式:for(auto &x:ar)

如果不是内置类型而是自定义类型,如Object ar[] = [1,2,3,4,5,6,7,8,9];如果我们不想改变数组中的值的话就加上const,如:for(const auto &x:ar)

5.友元没有自反性,没有传递性,也没有继承性

6.友元分为外部函数友元、成员函数友元和类友元

类友元

class Base;
class Object
{
private:
	int value;
public:
	Object(int x = 0):value(x) {}

	void fun(Base& x);
	void show(const Base& x);
};
class Base
{
    friend class Object;
private:
	int num;
public:
	Base(int x = 0) : num(x) {}
};

如果我想要让Object中的所有成员函数都能访问Base的公有、私有和保护属性成员,那么我如果一一进行友元函数说明就会很麻烦,所以我们可以使用类友元,注意:类友元可以声明在任何位置,友元独立于公有块和私有快,可以在Base类中加上friend class Object;

7.静态变量

int fun()
{
	static int x = 0;
	int y = 10;
	return x;
}
int main()
{}

x的可见性不变,即还是在fun函数内可见,但是他的生存期变长了,它存储在静态数据区,函数调用的时候,只初始化一次,再次调用的时候,就跳过这一条语句

那么fun函数返回可不可以用引用返回呢?

答案:可以。x的生存期长,当fun函数结束时,x依然存在,当变量的生存期不受函数的影响的时候,可以用引用返回

全局静态变量和静态函数的生存期是从定义的位置开始到程序结束,可见性是在定义的文件中可见,在整个工程的其他文件中不可见, 全局静态变量存储还是在静态数据区,静态函数在代码区

在类中,不允许在构造函数中和拷贝构造函数的初始化列表中对静态成员进行构建,所以静态成员只能在类内定义,类外初始化,如:int Object::num = 0;

类内定义的静态成员,是所有对象共享的,只有一份,普通成员每个对象都有一份

void Print() const 
{
	num+=10;
	value+=10;
	cout << "value  " << this->value << "num:  " << num << endl;
}

上面的常方法中,num+=10;和value+=10;可以编译通过吗?
答案:num+=10;可以编译通过,因为静态成员不依赖this指针,所以是可以的,而value+=10;不能编译通过,因为有const修饰,为常方法,不能在函数内部修改成员变量的值

静态函数static int show() {} static修饰的是函数本身,说函数是一个静态函数,而不是修饰返回类型int

下面静态函数中num+=10;能否编译通过?

static int Show()
{
	num+=10;
	cout << "value  " << this->value << "num:  " << num << endl;
}

答案:num+=10;可以编译通过,cout << "value " << this->value << "num: " << num << endl;不能编译通过
原因:静态函数中没有this指针,所以在静态函数中只能访问静态成员,不能访问普通成员,所以this->value不能编译通过

那么能不能把static int Show()定义成static int Show() const呢?

答案:不能
静态函数没有this指针,有this指针才能加const,从而变成常方法

下面哪种情况能编译通过,哪种情况不能编译通过?
在这里插入图片描述
答案:AB不可以,CD可以
A不可以,A会出现无穷的递归,Object obja中有obja.value和obj,obj中又有value和obj,obj中又有value和obj…
在这里插入图片描述
B不可以,引用必须引用一个已经存在对象,即引用必须初始化
C可以,指针可以不指向任何对象,即可以不初始化
D可以,静态成员不属于对象,构建对象的时候,这个对象只有value这个成员变量,而没有obj这个成员变量

单例模式:只能创建一个对象,那么如何实现呢?

class Object
{
private:
int value;
static Object instance;
static int num;
private:
Object(int x = 0):value(x) {}
Object(const Object& obj) = delete;//告诉编译器不用帮我生成默认的拷贝构造函数,我给删了
Object & operator=(cosnt Object& ob) = delete;
public:
static Object& GetInstance()
{
return instance;
}
};
int Object::num = 0;
Object Objcet::instance(10);//第一个Object代表类型

能不能把static Object& GetInstance()写成static Object GetInstance()让他以值的形式返回呢?

答案:不能
返回的时候会调用拷贝构造函数来创建临时对象,但是我们把拷贝构造函数删了,所以是不能的

此种方式创建的单例模式,我们称之为线程安全

8.下面代码在编译的时候就已经在数据区创建静态变量a,并初始化为10了,而不是在调用funa函数的时候才进行创建

void funa(int x)
{
	static int a = 10;
}
int main()
{
	funa(10);
}

下面程序在编译的时候会先在数据区创建静态变量b,同时会有一个标志位置为0,标志是否进行过初始化,当调用funb函数时,给b初始化,同时将标志位置为1,当再次调用funb函数时,如果标志位为1,说明已经初始化过了,就不再进行初始化了

void funb(int x)
{
	static int b = x;
}
int main()
{
	funb(10);
}

这种情况会导致线程不安全,必须要加锁,不安全的地方就是在标志位这个点上,如线程1调用funb函数并将b初始化成10,然后将标志位标记为1之前执行线程2,线程2调用funb函数,发现标志位此时不为1,就会将b置为20,这时,就会将线程1初始化的值覆盖掉了,然后线程2将标志位标记为1,回到线程1中,将标志位标记为1

9.加锁头文件#include 线程头文件#include

thread tha(funa);
thread thib(funb);
tha.join();
thb,join();

10.什么时候需要单例模式,如Windows桌面的回收站,我们只能打开一个回收站,还有构建内存池对象和线程池对象,只能构建一个

11.饥汉模式(饿汉模式)和懒汉模式

12.函数模板

函数模板可以用来创建一个通用功能的函数,以支持多种不同形参,简化重载函数的设计

模板定义:
template<模板参数表>
返回类型 函数名(形参列表) {…}//函数体

<模板参数表>(template parameter list)尖括号中不能为空,参数可以有多个,用逗号分开

模板参数主要是模板类型参数,模板类型参数代表一种类型,由关键字class或typename(建议用typename)后加一个标识符构成,在这里两个关键字的意义相同,他们表示后面的参数名代表一个潜在的内置或用户定义的类型

总结:函数模板根据一组实际类型或值构造出独立的函数的过程通常是隐式发生的,称为模板实参推演

为了判断用作模板实参的实际类型,编译器需检查函数调用中提供的函数实参的类型,该过程称为模板实参推演

在编译main函数中,由编译函数模板生成的函数,称为模板函数

下面程序fun(x);和fun(&x);推演出T的类型是什么?

template<class T>
void fun(T a)
{
	T x,y;
	cout << "T type:" << typeid(T).name << endl;
	cout << "a type:" << typeid(a).name << endl;
}
int main()
{
	int x = 10;
	fun(x);
	fun(&x);
	return 0;
}

fun(x):
typedef int T;
void fun(T a);//void fun(int a)

fun(&x):
typedef int * T
void fun(T a)void fun<int * >(T a)

那么把int x = 10;改成const int x = 10;后,推演出来的类型是什么?

fun(x)->int类型,值传递,a的改变不会影响到实参,就不需要const修饰
fun(&x)->const int * 类型,指针传递,可能会改变实参,就需要const修饰

下面程序推演出的类型是什么?

template<class T>
void fun(T a)
{
	T x,y;
	cout << "T type:" << typeid(T).name << endl;
	cout << "a type:" << typeid(a).name << endl;
}
int main()
{
	int x = 10;
	const int y = 10;
	int *xp = &x;
	const int* yp = &y;
	fun(xp);
	fun(yp);
}

fun(xp);->int *
fun(yp);->const int *

下面程序能否进行推演?

template<class T>
void fun(T* a)
{
	T x,y;
	cout << "T type:" << typeid(T).name << endl;
	cout << "a type:" << typeid(a).name << endl;
}
int main()
{
	int x = 10;
	const int y = 10;
	fun(x);
	fun(y);
}

答案:不能
因为我们明确指出void fun(T* a)应该是一个指针类型,所以会推演失败

13.完全泛化、部分特化和完全特化

templatevoid fun(T a)称为完全泛化,意思就是说只要是个类型就行,无论是int类型还是int * 类型,都可以

templatevoid fun(T* a)称为部分特化,意思是我只能接收指针类型,范围比完全泛化要小

template<>
void fun<char * >(char * a) {} 称为完全特化,意思是我只能接收特定的类型,如本例的char * 类型, 其他类型都不行

14.下面程序会推演出什么类型?

template<class T>
void fun(T* a)
{
	T x,y;
	cout << "T type:" << typeid(T).name << endl;
	cout << "a type:" << typeid(a).name << endl;
}
int main()
{
	int x = 10;
	const int y = 10;
	fun(&x);
	fun(&y);
}

fun(&x):T->int,a->int *
fun(&y):T->const int,a->const int * 注意:这种情况下x和y相当于const int x,y;是不能编译通过的,x和y必须初始化,定义一个随机值的常变量是没有意义的

15.判断下面推演出的类型是什么?

template<class T>
void fun(const T* a)
{
	T x,y;
	cout << "T type:" << typeid(T).name << endl;
	cout << "a type:" << typeid(a).name << endl;
}
int mian()
{
	int x = 10;
	const int y = 20;
	fun(&x);
	fun(&y);
	return 0;
}

fun(&x);T->int a->const int *
fun(&y);T->int a->const int *

16.C11中的右值引用,移动赋值,完美转换形成了一个铁三角,目的是使程序的效率更高,原来C++是根据类型不同进行函数重载的,但是在遇到函数返回值无名实体时,没有办法在做出函数重载的区分,所以在C11标准引入新的函数重载的依赖方式,增加了左值(lvalue)、将亡值(xvalue)、右值(rvalue)、纯右值(prvalue)的概念,进一步区分函数重载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值