静态成员
写在前面
在类定义中,它的成员(包括成员变量和成员函数),这些成员可以用关键字static
声明为静态的,称为静态成员。
不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享。
1.静态成员变量
在一个类中,若将一个成员变量声明为static
,这种成员变量称为静态成员变量。与一般的数据成员不同,无论建立多少个对象,都只有一个静态数据的拷贝。静态成员变量,属于某个类,所有对象共享。
静态变量,是在编译阶段就分配空间,对象还没创建,就已经分配空间。
#include <iostream>
using namespace std;
class Person
{
public:
static int m_Age;
private:
static int m_other;
};
int Person::m_Age = 100;
int Person::m_other = 20;
void test()
{
Person p1;
p1.m_Age = 10;
cout << "p1=" << p1.m_Age << endl;
cout << "p1的地址为:" << &p1.m_Age << endl;
Person p2;
p2.m_Age = 20;
cout << "p1=" << p1.m_Age << endl;
cout << "p2=" << p2.m_Age << endl;
cout << "p2的地址为:" << &p2.m_Age << endl;
}
int main()
{
test();
}
输出结果如下图:
通过输出结果我们可以很清除地看到,静态成员变量是所有对象共享的。任何一个对象都可以改变静态变量的值,但是静态变量不属于任何一个对象。
下面我们看一下对象的大小,以此来说明静态数据成员不属于某个对象,在为对象分配的空间中不包括静态成员所占的空间。
#include <iostream>
using namespace std;
class Person
{
public:
static int m_Age;
private:
static int m_other;
};
int Person::m_Age = 100;
int Person::m_other = 20;
void test()
{
Person p;
cout << sizeof(p) << endl;
cout << sizeof(p.m_Age) << endl;
}
int main()
{
test();
}
输出结果如下图:
我们可以看出,对象的大小为1,而静态成员佰利联的大小为4。
当我们尝试去访问m_other时,编译器会报错,因为它是一个私有数据成员,这也说明了静态成员变量是有权限控制机制的。
拷贝构造函数
写在前面
在之前的博文中,我介绍了构造函数和析构函数,这一篇里面,我会对拷贝构造函数单独进行介绍,并介绍深拷贝、浅拷贝等。
1.拷贝构造函数的调用时机
拷贝构造函数的调用时机如下:
下面,我举例说明:
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass()
{
cout << "默认构造函数" << endl;
}
MyClass(int a)
{
cout << "有参构造函数" << endl;
}
MyClass(const MyClass &m)
{
cout << "拷贝构造函数调用" << endl;
}
~MyClass()
{
cout << "析构函数调用" << endl;
}
int ak;
};
void test()
{
MyClass m1;
m1.ak = 10;
MyClass m2(m1);//用已经创建好的对象来初始化新的对象
}
int main()
{
test();
}
输出结果如下图:
在上面的例子中,我使用了一个已经创建好的对象来初始化一个新的对象。 这也就是拷贝构造函数对的第一个调用时机。
下面是第二个调用时机,即对象以值传递的方式给函数参数。
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass()
{
cout << "默认构造函数" << endl;
}
MyClass(int a)
{
cout << "有参构造函数" << endl;
}
MyClass(const MyClass &m)
{
cout << "拷贝构造函数调用" << endl;
}
~MyClass()
{
cout << "析构函数调用" << endl;
}
int ak;
};
void dowork(MyClass m)//以值传递方式给函数传参
{
}
void test()
{
MyClass m1;
m1.ak = 10;
dowork(m1);
}
int main()
{
test();
}
输出结果如下图:
最后,是函数局部对象以值传递的方式从函数返回。
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass()
{
cout << "默认构造函数" << endl;
}
MyClass(int a)
{
cout << "有参构造函数" << endl;
}
MyClass(const MyClass &m)
{
cout << "拷贝构造函数调用" << endl;
}
~MyClass()
{
cout << "析构函数调用" << endl;
}
int ak;
};
MyClass dowork()//函数局部对象以值传递的方式从函数返回
{
MyClass m1;
return m1;
}
void test()
{
MyClass m = dowork();
}
int main()
{
test();
}
输出结果如下:
2.浅拷贝和深拷贝
浅拷贝
同一个类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为浅拷贝。
一般情况下,浅拷贝没有任何副作用,但是当类中有指针,并且指针指向动态内存分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
}
Person(const char *name, int age)
{
Name = (char*)malloc(strlen(name) + 1);
strcpy (Name, name);
Age = age;
}
~Person()
{
cout << "析构函数调用" << endl;
if (Name != NULL)
{
free(Name);
Name = NULL;
}
}
//系统提供默认拷贝构造函数
char *Name;
int Age;
};
void test()
{
Person p1("Aoki", 20);
Person p2(p1);
}
int main()
{
test();
}
输出结果如下:
我们可以看到,错误提示信息中提到了heap.cpp。此外,输出结果中出现了两次析构函数的调用。下面,我尝试尽可能地解释清楚原因。
我们创建了一个对象p1,它有两个属性,一个存放在堆区,一个在栈区,当编译器进行简单的值拷贝时,只是将堆区内容的地址给了新对象p2,接着调用析构函数,释放p1的空间,而存放在堆区的数据也就消失了,紧接着,对象p2也调用析构函数来进行释放。那么,堆区的同一个地址就被释放了两次,引发了异常。图示如下:
这时候,我们就要用到深拷贝了。
深拷贝
当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定义拷贝构造函数,自行给指针动态分配空间,深拷贝。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
}
Person(const char *name, int age)
{
Name = (char*)malloc(strlen(name) + 1);
strcpy (Name, name);
Age = age;
}
Person(const Person &p)//深拷贝
{
Age = p.Age;
Name = (char*)malloc(strlen(p.Name) + 1);
strcpy(Name, p.Name);
}
~Person()
{
cout << "析构函数调用" << endl;
if (Name != NULL)
{
free(Name);
Name = NULL;
}
}
char *Name;
int Age;
};
void test()
{
Person p1("Aoki", 20);
Person p2(p1);
}
int main()
{
test();
}
输出结果如下:
函数重载
写在前面
如果同一个作用域内的几个函数名字相同,但是形参列表不同,我们称之为重载函数。
实现函数重载的条件:
函数的名字仅仅是让编译器知道它调用的是哪个函数,而函数重载在一定程度上可以减轻程序员起名字、记名字的负担。
1.定义重载函数
下面是一个函数重载的例子。函数重载使得我们可以定义一组函数,名字都为func。
```c++
#include
namespace A { void func() { cout « “没有参数” « endl; } void func(int a) { cout « “a=” « a « endl;
}
void func(string a)
{
cout << "a=" << a << endl;
}
void func(int a, float b)
{
cout << "a=" << a << " b=" << b << endl;
}
void func(string a, int b)
{
cout << "a=" << a << " b=" << b << endl;
}
}
void test() { A::func(); A::func(15); A::func(“Aoki”); A::func(12, 3.12); A::func(“Aoki”, 15); }