对象特性
构造函数的调用方式
#include<iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "无参构造函数调用" << endl;
}
Person(int age)
{
m_Age = age;
cout << "有参构造函数调用" << endl;
}
Person(string name, int age)
{
m_Age = age;
m_Name = name;
cout << "有参构造函数调用" << endl;
}
Person(const Person &p)
{
m_Age = p.m_Age;
cout << "拷贝构造函数调用" << endl;
}
~Person()
{
cout << "析构函数调用" << endl;
}
private:
string m_Name;
int m_Age;
};
void test10()
{
//栈上实例化调用
//1.括号法
Person p1;
Person p2(10);
Person p3("张三",20);
Person p4(p2);
//2.显式法
Person p5 = Person(10); //Person(10)是匿名对象,当前行结束,马上析构。
Person p6 = Person{ "张三", 18 };
Person p7 = Person(p5);
//3.隐式法
Person p8 = 10; //Person p6=Person(10)
Person p9 = p8;
Person p10 = { "张三", 18 };
}
void test11()
{
//堆上实例化调用
//1.括号法
Person *ptr1 = new Person;
Person *ptr2 = new Person();
Person *ptr3 = new Person(10);
}
int main()
{
test10();
//test11();
system("pause");
return 0;
}
深拷贝和浅拷贝
C++利用构造函数、拷贝构造函数和析构函数进行初始化和清理的操作,是必须有的实现。默认情况下,编译器会给一个类提供
1)默认构造函数(无参、空实现)
2)默认析构函数(无参、空实现)
3)默认拷贝构造函数(值拷贝、浅拷贝)
4)赋值运算符operator=(对属性进行值拷贝、浅拷贝)
如果有属性开辟在堆区,也需要程序员使用delete调用析构函数手动释放内存。但当使用默认拷贝构造函数、默认赋值运算符时,会带来内存重复释放(浅拷贝)的问题。一定要重写析构函数(释放堆区内存) 、拷贝构造函数(防止浅拷贝)、赋值运算符重载(防止浅拷贝)、构造函数(如果自己写了拷贝构造函数,编译器不会自动调用默认构造函数)。
如果有属性开辟到堆区,在使用多态时,要使用虚析构 函数或纯虚析构函数。
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age, int height)
{
m_Age = age;
m_Height = new int(height);
cout << "构造函数调用" << endl;
}
Person(const Person &p)
{
m_Age = p.m_Age;
m_Height = new int(*p.m_Height);
cout << "拷贝构造函数调用" << endl;
}
~Person()
{
if (m_Height != NULL)
{
delete m_Height;
m_Height = NULL;
}
cout << "析构函数调用" << endl;
}
public:
int m_Age;
int *m_Height;
};
void test21()
{
Person p1(18, 160);
Person p2(p1);
cout << "p1年龄:" << p1.m_Age << ", p1身高:" << *p1.m_Height << endl;
cout << "p2年龄:" << p2.m_Age << ", p2身高:" << *p2.m_Height << endl;
}
int main()
{
test21();
system("pause");
return 0;
}
类对象作为类成员
1)在使用A类对象作为B类成员时,A的类中需要默认构造函数。以初始化列表的方式创建构造函数B类时,A类不需要默认构造函数。因此,当我们创建一个构造函数时,最好也添加一个默认构造函数,防止编译报错。
#include<iostream>
using namespace std;
#include<string>
class Phone
{
public:
/*Phone()
{
cout << "Phone无参构造函数调用" << endl;
}*/
Phone(string phoneName)
{
m_PhoneName = phoneName;
cout << "Phone有参构造函数调用" << endl;
}
~Phone()
{
cout << "Phone析构函数调用" << endl;
}
public:
string m_PhoneName;
};
class Person
{
public:
//Phone m_Phone=phoneName;
Person(string personName, string phoneName):m_PersonName(personName),m_Phone(phoneName)
{
/*m_PersonName = personName;
m_Phone=phoneName;*/
cout << "Person构造函数调用" << endl;
}
~Person()
{
cout << "Person析构函数调用" << endl;
}
public:
string m_PersonName;
Phone m_Phone;
};
void test31()
{
Person p("张三", "华为");
cout << p.m_PersonName << "拿着" << p.m_Phone.m_PhoneName << endl;
}
int main ()
{
test31();
system("pause");
return 0;
}
代码分析
1)初始化列表的方式创建构造函数B类时,A类不需要默认构造函数。执行流程:
2)非初始化列表的方式创建构造函数B类时,A类需要默认构造函数。执行流程:
对象模型
1)在C++中,类内的成员变量和成员函数分开存储。非静态成员变量属于类的对象;静态成员变量和静态成员函数,所有对象共享(可以通过对象或者类名访问),存储在全局区;非静态成员函数,也只有一份函数实例,通过this指针区分哪个对象调用。
2)常对象和常函数不可以修改属性的值。非静态成员函数隐含一个this指针,this指针的本质是指针常量(指针的指向不可以更改,指向的值可以更改),用以区分调用的对象。非静态成员函数用const修饰变成常函数,使得this指针指向的值也不可以更改。常对象只能调用常函数。
#include<iostream>
using namespace std;
class Person
{
public:
void getAB1()
{
cout << m_A << "," << m_B << endl;
}
void getAB2() const
{
cout << m_B << endl;
}
//非静态成员函数
void func1(int a, int b)
{
this->m_A = a;
this->m_B = b;
cout << "func1调用" << endl;
}
//常函数
void func2(int a, int b) const
{
//this->m_A = a; 常函数不可修改对象属性的值。
this->m_B = b;
cout << "func1调用" << endl;
}
//静态成员函数
static void func3()
{
//m_A = 0;静态成员函数只能访问静态成员变量。
m_C = 0;
cout << "func2调用" << endl;
}
private:
//非静态成员变量
int m_A;
mutable int m_B;
//静态成员变量
static int m_C;
};
int Person::m_C = 100;
void test41()
{
Person p1;
p1.func1(10, 10);
p1.getAB1();
//静态成员属于所有对象共享,可以通过对象或者类名访问。
Person::func3();
p1.getAB1();
}
void test42()
{
const Person p1;
//p1.func1(10, 10);
p1.func2(20, 20);
p1.getAB2();
}
int main()
{
//test41();
test42();
system("pause");
return 0;
}