// 默认生成的构造函数
class test
{
public:
test(){} 构造函数
test(const test& rhs){} 拷贝构造函数
test& operator=(const test& rhs){} 操作符=重载
~test(){} 析构函数
};
//copy assign操作符有时候是会被拒绝调用的
class test
{
public:
test(int data)
:b(data){}
const int b;
}
test A(1);
test B(2);
A = B; //报错,企图修改const成员变量,是不允许的
class test
{
private:
/* data */
test(const test &rhs)
{
this->data = rhs.data;
};
public:
test(/* args */){};
~test(){};
int data;
};
int main(){
test A;
test B(A); // error: 'test::test(const test&)' is private within this context test B(A);
return 0;
}
class base
{
public:
base(/* args */){};
virtual void fun()
{
cout << "base fun" << endl;
}
virtual ~base()
{
cout << "base delete" << endl;
};
};
class componet : public base
{
public:
void fun()
{
cout << "componet fun" << endl;
}
~componet()
{
cout << "componet delete" << endl;
};
};
int main(){
componet A;
base *p = &A;
delete p;
return 0;
}
输出
componet delete
base delete
不加virtual,并不会调用派生类的析构,只会打印
base delete
class widget
{
public:
const widget& operator=(const widget& rhs)
{
this->data = rhs.data;
return *this;
}
int data;
};
int main(){
widget A;
widget B;
widget C;
A = B = C; //opertor=返回*this才支持连续赋值
(A=B) = C; //如果不加const 这里不会报错
return 0;
}
class widget
{
public:
widget &operator =(const widget &rhs)
{
delete this->a;
this->a = new int(*rhs.a); //此时rhs.a已经被释放了,解引用一个已释放的指针会发生意外的错误
return *this;
}
int *a;
};
int main()
{
widget A;
A = A;
return 0;
}
所以我们需要在赋值操作符里处理自我赋值,修改赋值操作符重载的特殊情况如下:
class widget
{
public:
widget &operator =(const widget &rhs)
{
if(this == &rhs) return *this;
delete this->a;
this->a = new int(*rhs.a);
return *this;
}
int *a;
};
int main()
{
widget A;
A = A;
return 0;
}
class customer{
public:
customer(){}
customer(const customer &rhs)
:name(rhs.name)
{
}
string name;
int age;
};
class priorityCustomer:public customer{
public:
priorityCustomer(){}
priorityCustomer(const priorityCustomer &rhs)
:customer(rhs), priority(rhs.priority)
{
}
int priority;
};
int main()
{
priorityCustomer tmp;
tmp.name = "bob";
tmp.age = 10;
tmp.priority = 1;
priorityCustomer component(tmp);
cout << "_name:"<< component.name << " ,_age:" << component.age << endl;
return 0;
}
输出
_name:bob ,_age:0
这样设计的拷贝函数在派生类实现的时候会造成基类的成员变量未全部复制,造成意外的错误。所以在设计到copy类的函数时应该确保“复制对象内的每一个成员”
class Investment{}; //定义一个要操作的类
Investment *createInvestment(){}; //假如该函数申请返回一个有效指针
pInv1 = createInvestment();
... //如果这时候就退出了就会造成内存的泄漏
delete pInv1;
#最好的解决方法就是将该指针交给智能指针管理
std::auto_ptr<Investment>pInv1(creatInvsetment()); //这样我们只需要用并不在意在哪里去释放,因为auto_ptr这个模板类中已经实现对其管理的指针的释放
值得注意的是auto_ptr已经在C++11中被弃用了,本条只讲解资源管理的具体方式,不对几种智能指针做详细的解释 。
auto_ptr使用了一种"管理权转移"的思想,在使用拷贝或者赋值时会将原对象置为NULL, 所以在完成拷贝或者赋值后就不要再使用该指针
//auto_ptr的实现原型
template<typename T>
class auto_ptr
{
public:
//不允许隐式构造
explicit Mauto_ptr(T* ptr = NULL)//explicit可禁止隐式构造
{
_ptr = ptr;
}
auto_ptr(const auto_ptr& src)//拷贝构造
{
if (&src == this)//防止自拷贝
{
return;
}
_ptr = src._ptr;
src._ptr = NULL;
//这是auto_ptr拷贝构造的最大特点,新的智能指针=拷贝构造成功之后,会将原智能指针置空
}
auto_ptr& operator=(const auto_ptr& src)
{
if (this == &src)//防止自赋值
{
return *this;
}
_ptr = src._ptr;
src._ptr = NULL;//和拷贝构造一样,赋值完成之后,会将原智能指针置空
return *this;
}
~auto_ptr()//auto_ptr的析构直接调用delete就行
{
delete _ptr;
}
private:
T* _ptr;
};
int main()
{
auto_ptr<int> ap1(new int(1));
auto_ptr<int> ap2(ap1); //此时会将ap1中的指针置为nullptr
*ap1 = 2; //此时就会crash
return 0;
}
shared_ptr 是通过引用计数的方式来实现多个shared_ptr对象之间的资源共享。通俗来说就是允许多个shared_ptr指针之间的互相赋值,它的基本原理就是记录对象被引用的次数,当引用次数为 0 的时候,也就是最后一个指向某对象的共享指针析构的时候,共享指针的析构函数就把指向的内存区域释放掉。
std::shared_ptr<int>p1(new int(5));
cout << "count:"<< p1.use_count() <<endl; //count:1
std::shared_ptr<int>p2(p1);
cout << "count:"<< p1.use_count() <<endl; //count:2
p2.reset();
cout << "count:" << p1.use_count() <<endl; //count:1
从上面就不难看出,shared_ptr当涉及copy时采用的就是引用计数的方式,它允许多个对象之间的拷贝,只要不是所有的对象都释放,总会保存当初指向的那个内存,就像这里new int(5)是一直会在堆上存在的的,当生成p2时只是对原有的shared_ptr对象计数加1而已,然后p2仍然指向的还是这个new int(5),同理当p2被释放时也只是计数减1,不会去释放new int(5)申请出来的内存,只有当p1也被释放的时候,计数等于0,才会去释放new int(5)的内存
另外要注意的是智能指针的构造函数默认是explict类型的,禁止隐式转换,像 std::shared_ptrp1 = new int(5)这种隐式的转换是会报错的
class component{
public:
explicit component(int data)
:value(data){}
int value;
};
component *creatComponent()
{
component *p1 = new component(100);
return p1;
}
void fun(component *p)
{
}
int main()
{
std::shared_ptr<component>ptr1(creatComponent());
fun(ptr1.get()); //通过其内部的get函数获得原始指针
fun(ptr1); //报错,其无法直接隐式转换成原始指针
cout << (*ptr1).value << endl; //其重载了operator*,可以直接访问原始指针内部
cout << ptr1->value << endl; //其重载了operator->,可以直接访问原始指针内部
string *p = new int[100];
delete p; //这样就会导致只析构了一个int类型,应该是delete []p;
processWidget(std::tr1::shared_ptr<widget>(new widget),priority())
在这个函数中,我们的参数执行主要有三步:
可以知道是" new widget"是最先执行的,然而调用priority() 和 调用shared_ptr的构造函数 谁先谁后在不同的编译器下处理的情况可能都有所不同,如果priority()先行调用出现了crash,则会造成的问题就是" new widget"没有交给智能指针去管理,从而导致了内存泄漏,所以当智能指针管理一个指针时一定要以一个独立的语句去管理,这样我们可以修改如下:
std::tr1::shared_ptr<widget>pw(new widget)
processwidget(pw, priority)
class person{
public:
person(){
cout << "person()" << endl;
};
virtual ~person(){};
};
class student: public person{
public:
student()
{
cout << "student()" << endl;
};
student(const student& another)
{
cout << "student copy" << endl;
}
~student(){};
};
void fun(student s){}
int main() {
student bob;
fun(bob);
return 0;
}
运行后输出:
person()
student()
person()
student copy
如果将 void fun(student s){} 改为 void fun(student& s){},则只会输出:
person()
student()
避免了形参在获取值得拷贝构造,已经调用基类的构造
另一点的切割问题就是,当用基类做形参,传递一个派生类时也只会调用基类中的函数,并不会调用派生类中的函数,无法行成多态,例如:
class person{
public:
virtual void test()
{
cout << "person.test() "<< endl;
}
};
class student: public person{
public:
void test()
{
cout << "student.test()" << endl;
}
};
void test(person s)
{
s.test();
}
输出:
person.test()
由于形参时基类,不是引用或者指针无法形成多态,调用的还是基类的test函数
1. a是个局部变量返回时会被释放
int& test()
{
int a = 10;
return a;
}
2. static造成了函数中就只会存在这一个对象,当它的值被改时,所有调用这个函数的都会全部被改成一样的,因为他们的引用都指向这个对象
int& test(int b)
{
static int a = b;
return a;
}
下面的判断会永远相等,不管传什么数
if(test(1) == test(2))
{
cout << "yes" << endl;
}
3. 会造成资源泄漏,因为没有调用delete
int& test()
{
int *a = new int(10);
return *a;
}
正确的写法就是直接传值,这是以int类型来讲解,假如int是个类,就是会多一步构造和析构,但这总比上面几种方法要好的多
int test()
{
int a = 10;
return a;
}