C++相关

本文详细介绍了C++中的智能指针my_shared_ptr的实现,包括构造、赋值、析构等功能。同时讨论了多态的概念,虚函数表的作用以及析构函数为虚函数的重要性。此外,还提到了C++中的const关键字、常量指针以及函数重载的规则。最后,简述了STL中的空间配置、迭代器、容器如vector、list、deque、map的内部实现和扩容策略,以及哈希表和解决冲突的方法。
摘要由CSDN通过智能技术生成

1.智能指针

#include<iostream>

#include<string>
#include<map>
template<class T>
class my_shared_ptr
{
private:
int* count;
T *m_ptr;
public:
my_shared_ptr():count(new size_t),m_ptr(NULL) {};
my_shared_ptr(T *ptr) :m_ptr(ptr), count(new int(1)) {
}
my_shared_ptr(my_shared_ptr &other) :m_ptr(other.m_ptr), count(&(++*other.count)) {


}
~my_shared_ptr()
{
--(*count);
if (*count == 0)

delete m_ptr;
m_ptr = NULL;
delete count;
}
}
my_shared_ptr &operator=(my_shared_ptr &other)
{
if (this == &other)
return *this;
++other.count;
--this->count;
if (count == 0)
{
delete m_ptr;
delete count;
}
m_ptr = other.m_ptr;
count = other.count;
return *this;
}
T &operator *()
{
return *m_ptr;
}
T *operator ->()
{
return m_ptr;
}
int getref()
{
return *count;
}
};


using namespace std;
int main()
{
my_shared_ptr<string> pstr(new string("abc"));
my_shared_ptr<string> pstr2(pstr);
cout<<pstr.getref();
cout << pstr2.getref();
my_shared_ptr<string> pstr3(new string("hao"));
    pstr3 = pstr2;

}

1.多态是为了接口的复用,基类定义虚函数,子类选择继承或覆盖实现这个函数,在调用时根据对象不同选择调用不同的实现版本,这就是多态。多态的实现是由虚函数表实现的。

2.基类只有一份虚函数表,添加虚函数就会增加表项。子类继承之后,会把虚函数表继承过来,同时还会有自己的虚函数,实现基类中的方法会覆盖相应的表项。

3.析构函数定义为虚函数,主要是基类中定义续析构函数,如果一个基类指针指向子类对象,如果基类不是虚析构函数,删除这个指针不会调用子类的析构函数,析构函数用来释放资源,这样就会有泄露内存的危险。

4.const定义之后,它的值不能改变,如 const int a=1;const int &b=a; b=10(这个操作是不允许的,不能通过常量引用改变指向的值)

5.常量指针形如const int *a = &b; a是一个指向常量的指针,不能通过指针a来改变b的值

6.指针常量,int *const a =&b,指针a的指向不能变,但是b的值可以变,是con.yst来修饰指针的。 

7.函数重载只能通过参数列表不同来区分,不能通过返回值,因为c++可以忽略函数返回值,还可以通过const。

STL

    1.空间配置,空间不单指内存,>127字节采用一级空间配置器,使用malloc和free释放,小于128字节采用二级空间配置器。二级空间配置器维持了16个链表,从8字节到128字节,从链表中分配,链表空间不足,求助于内存池,内存池填充链表,如果内存池不够,内存池会先把已有的空间合并一下,返回给链表,如果实现没有空间,就在堆上分配,堆上分配不了的,返回异常,内存分配失败。

    2.迭代器是一种智能指针,迭代器模式使得我们可以顺序访问聚合对象的元素,不需要暴露对象的实现细节。trait表示萃取,迭代器需要有相应的型别,c++可以使用typeid鉴定类型,但不可用来声明。可以采用模板的参数推导机制,但是返回值的类型无法指定,可以声明内嵌型别,但是无法适用于原生指针,为此采用了偏特化处理,在模板参数上又加了一层条件,用来萃取参数型别,迭代器型别有value type point type 等

    3.vector是以二倍方式扩容,但是也不一定,注意vector的扩容会导致迭代器失效,vector的插入会插入点之后的迭代器失效。list是双向链表,deque是由一段一段的连续空间构成,数组中存节点的指针,指针指向一段空间,分配和释放就在二端扩容,stack和queue是deque的适配表现,封装了一层皮。map的底层实现是红黑树,红黑树插入,如果是根节点,直接插入,如果是黑节点,直接插入,如果是父节点是红节点,并且叔父节点为红节点,直接变换颜色,如果叔父节点为黑色,要分插入点为左节点还是右节点,左节点直接左旋,右节点需先左旋,后右旋。

4.解决冲突的方法:线性探测、二次探测、开链。删除采用惰性删除,只标记不删除。二次探测采用指数的方式散列,减少了冲突多次发生的概率。hashtable以开链的方式处理冲突,hash函数对于int char long等直接返回键值,对于字符串进行转换,hash大小为质数,要不然会导致hash数之间强烈的关系,导致分布不均匀,增大了冲突的概率。表格重建,会重新指定下一个素数大小空间,然后重新计算hash来计算位置

5.仿函数是定义一个对象,然后重载括号,来实现适配。

实现memcpy函数主要考虑内存重叠问题。

因为dst的内存起始地址可能处在src+length所在的内存范围内,所以需要对不同的情况进行区分

1.对于dst <src和dst>src+length二种情况下不存在内存重叠问题,所以直接拷贝

2.对于内存重叠,需要从后端拷贝,这样dst是不会覆盖还未拷贝的src数据。

#include<iostream>
using namespace std;
void * my_memcpy(char  *dst,const void  *src, int length)
{
	if (src == nullptr || length <= 0)//边界值判断
		return nullptr ;
	char *d = nullptr;
	char *s = nullptr;
	
	if (dst < src || dst > ((char*)src + length))//没有重叠
	{
		s = (char *)src;
		d = (char *)dst;
		while (length--)
		{
			*d = *s;
			s++;
			d++;
		}
	}
	else//内存重叠
	{
		s = (char*)src + length - 1;
		d = (char*)dst + length - 1;
		while (length--)
		{
			*d = *s;
			s--;
			d--;
		}
	}
	return d;
}
void main()
{
	char a[10];
	char *p = "11111111";
	my_memcpy(a, p, 10);
	cout << a << endl;
}
effective c++笔记
1.c++相比c添加了面向对象,模板,STL,是对C的一个升级,也同时兼容C语言。
2.方法中的const变量存在于栈中,全局变量的const存在于常亮区。const会出现在符号表中,而define不会出现在符号表中,会导致调试不方便,define在编译期完成替换
3.const作用于方法,表示方法不能修改成员变量,不能调用非const函数,可以用来重载函数,const会将成员函数也加上const *this。成员函数中static和const是冲突的,如果const和非const函数功能一样,可以让非const调用const函数,使用二次转型。
4.内置对象手工初始化,构造函数内初始化先初始化在赋值,比列表初始要多一步操作,效率低。成员初始化顺序同声明顺序
5.如果不定义,  c++默认定义default构造函数,复制构造函数,拷贝赋值构造函数,析构函数
6.如果不想要使用编译器自动生成的函数,声明为private并不实现。
7.多态基类的析构函数应为虚函数。
8.别让异常逃离析构。如果逃离,在析构对象数组的时候产生异常回导致崩溃。如果必须可能抛出异常,要自己捕捉,并吞下异常。
9.不在析构和构造函数中调用虚函数,构造函数执行时,还是基类对象,不存在子类,调用基类函数。析构函数中调用时,子类已经析构,无法实现多态。
10.令 operator=返回一个*this的引用。
11.在opreator=中处理自我赋值,判断对象地址(this=&src)。如果考虑异常,就先调用复制构造函数复制对象,然后*this和复制对象交换。
12.复制对象要复制对象的一切,重写opreator=编译器是不会帮你检查的。 
13.使用智能指针对象来管理资源的建立和释放。
14.
15 
16.使用new[],使用delete[]释放,new[]表示数组,new表示一个,
17.不要在方法调用中这new一个对象,这样如果异常,这个new出来的对象就找不到了。
18.
19.
20.对象最好以引用const的方式传递,值传递会调用多次构造函数和析构函数,开销过大。
21.operator *要采用值传递,引用传递会导致问题。不能返回本地对象或变量的引用,因为函数返回之后对象被销毁。
22.成员变量最好以private封装,这样可以保持类的封装,隐藏内部实现。通过方法来控制变量读取,通过const方法来让变量只读。
23.以非成员,非友元函数来封装类中的方法,通过声明namespace来实现包含那个头文件 编译哪部分功能。
24.某个函数的所有参数都需要隐式转换,需要声明为非成员函数。explicit可以避免隐式转换。
25.
26.尽量延后变量的定义时间,并以列表的方式初始化,可以避免赋值构造函数的调用。
27.尽量少使用转型操作,const_cast用来去除对象的const dynamic_cast安全的向下转型 reinterpret_cast低级转型,通常用作int*到int  static_cast类似于c语言的转型,强制隐式转换
28.函数加入const代表返回值不可修改。避免返回函数内的局部变量
29.
30.内联函数不一定展开,某些递归循环和虚函数就不会展开。代码直接替换,体积变大,需考虑成本。 
31.声明与实现分开,可以减少编译的依存度。                                                                                                                                                                                                                                                                                                                                                       
32.public继承意味着is a的关系,要注意基类对象的操作要同样适用于子类对象,否则设计不合理,如所有鸟会飞,企鹅是鸟,企鹅也会飞。
33.如果基类中有重载函数,public继承之后,定义重名函数会遮盖重载版本,解决的方法就是使用using声明
34.函数接口继承是要在子类中声明的,而虚函数继承,不需要声明,会默认继承,所以如果子类没有根据需求定义虚函数而采用基类的虚函数实现,这将会带来问题。接口中的非虚函数通常强制子类继承,子类不需要改变。
35.
36.非虚函数静态绑定,指向某个对象的指针就会调用哪个对象的方法,不会发生多态。而虚函数会检查这个指针实际指向的对象,采用动态绑定。 
37.方法缺省参数值是静态绑定的,为了效率,在多态环境下,还是会使用基类的参数初始化多态的函数。
38.复合:类包含别的类、
40.单一继承和多重继承代价相同的情况下优先使用单一继承,虚继承编译器要付出更大的代价,虚继承解决多重继承中的菱形问题。
41.
42.typename和class相同,typename还用来指明嵌套类型的声明

43.

More Effective c++笔记

27. 对象只生成在堆中,将析构函数设置成private。对象只在栈中,operator new设置为private

28. Auto_ptr在复制之后,原对象会失效,以引用传递auto_ptr

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值