1.struct 的区别(C 和 C++):
C语言中只能定义变量。
C++语言中可以定义变量和函数。同时C++语言中,struct 中所有变量和函数都是 "public" 权限
2.类的封装:
3.类内部的三种权限
public:共有属性(修饰的成员变量和方法; 可以在类的内部和外部使用。)
private:私有属性(修饰的成员变量和方法,只能在类的内部使用,不能在类的外部使用)
protected:主要用于继承,保护属性(修饰的成员变量和方法; 可以在类的内部和继承的子类使用,不能在类的外部使用)
4.struct 和 class 的区别:
struct 中成员的默认权限为 public;
class 中成员的默认权限为 private;
5.类的调用(一个类调另一个类)
6.对象的构造和析构;
1)构造函数:"名称和类名相同";"没有返回值 "。"可以有多个,进行函数重载"
在内存开辟之后调用构造函数。
2)无参构造函数和有参构造函数(又分为两种)
1.无参构造函数:定义对象的时候,对象后不能加括号。如:class stu{ stu(){"无参构造函数"}}; stu st;(正确定义对象)。
如果加上括号: stu st(); 编译的时候不会报错,(因为编译器把它当作函数声明),运行的时候会报错。
2.有参构造函数:
"普通参数": 根据形参的类型和个数,也可以进行函数重载。
class Animal{
public:
Animal(int age)
{
cout << "一个参数构造函数数字!" << endl;
mName="undefined";
mAge=age;
}
Animal(string name)
{
cout << "一个参数构造函数字母!" << endl;
mName=name;
mAge=0;
}
Animal(string name,int age)
{
cout << "两个参数构造函数!" << endl;
mName=name;
mAge=age;
}
//拷贝(复制)构造函数 用一个对象来初始化另一个对象
Animal(const Animal& animal)
{ //拷贝构造函数,形参必须是引用。否则会造成死循环,一直调用它本身。本质上成了递归调用本身。
cout << "拷贝构造函数!" << endl;
mName=animal.mName;
mAge=animal.mAge;
}
"拷贝参数":用一个对象来初始化另一个对象
它的形参必须是引用。如果是普通变量,就会造成本质上的递归调用本身;无意义。
3)析构函数:"名称=类名+"~""; "没有返回值";"没有游戏参数"。"一个类只可以有一个析构函数"
在内存释放之前调用析构函数。
7.构造函数调用规则: (参考上面的代码)
1) 括号法;
Animal animal1("smith"); //一个参数构造函数字母!
Animal animal2(20); //一个参数构造函数数字!
Animal animal3("John",30); //两个参数构造函数!
Animal animal4(animal3); //拷贝构造
2)显示调用构造函数(匿名对象调用拷贝构造的问题)
Animal("Smith",30); //匿名函数生命周期仅限于当前行;此行运行完后立即析构。
Animal animal5=Animal(30); // 一个参数构造函数数字!;形参为普通变量
Animal animal5=Animal("smith",90); 注意:/"两个参数构造函数!;拷贝构造。这行比上一行多了一个拷贝构造,是因为string容器的缘故。只有这时才会有拷贝构造"
Animal animal5(animal(30)); //一个参数构造自考函数数字!。
Animal animal5(animal(30,"smith")); "两个参数构造函数!;拷贝构造。" 原因同上,当不调用 string 参数时,它不会调用拷贝构造。"是由编译器处理的,具体处理方式不清楚"
"上面那两种情况,调不调用拷贝构造函数,主要看参数类型。===有string:就调拷贝构造函数;====没有string: 就不调拷贝构造函数===="
匿名对象如果有变量来接,它就是一个对象;"此时这个变量就相当于匿名对象的名称。"
匿名对象如果没有变量来接,他就是一个实例化对象。
Animal4("john",92); Animal(animal4); 此时当这两句在一起,编译器会报错,重定义。
3)等号法
Animal animal5=10; //不常用。调用一个函数构造函数
Animal animal5=animal4l; “拷贝构造,常用
8.拷贝构造常用的两种调用方式:
Animal animal5=animal4l;
Animal animal5(animal4l);
9.拷贝构造的调用时机:
1)对象以值传递的方式传递给函数参数。 void func(Animal animal2){ }; 用实参初始化形参,调用了拷贝构造函数
2)用一个对象初始化另一个对象。Animal animal5=animal4l; 或者:Animal animal5(animal4l);
3)函数返回局部对象。(注意这里:debug模式 和release模式不一样)。
debug模式下:会调用拷贝构造,打印两个地址也不相同。
release模式下,不会调用拷贝构造,打印的地址相同,编译器这里做了优化,直接把原来的对象返回回去了。
Animal MyBussiness()
{
//局部对象
Animal animal; //无参构造
cout << &animal << endl;
return animal; //编译器先拷贝一份,然后返回。相当于返回一个匿名对象
}
void test03()
{
Animal animal=MyBussiness();
cout << &animal << endl;
}
10.构造函数调用规则:
默认情况下,编译器至少为我们写的类增加三个函数
1)默认构造函数(无参,函数体为空)
默认析构函数(无参,函数体为空)
默认拷贝构造函数,对类中非静态成员属性简单 "值"拷贝。
2) 如果用户定义了"拷贝构造函数",C++不会再提供"任何默认构造函数"。
3)如果用户定义了"普通构造,"C++"不会"再提供"默认无参构造",但是"会提供默认拷贝构造"。
4)在构造函数中调用构造函数是一个危险的行为。本质上:里面的构造函数相当于一个"匿名对象"。生命周期只有本行(没有变量接的情况下)。
11.初始化列表:初始化成员列表只能在构造函数使用。
"构造函数的初始化列表顺序:"
1)当一个类中组合了其他类的对象,先执行被组合的构造函数。
2)如果组合对象有多个,来自不同的类,根据定义的顺序进行构造函数的调用,而不是初始化列表的顺序。
3)“被组合对象的构造顺序:与定义顺序有关系,与初始化列表的顺序,没有关系”
4)若类成员中有 const 修饰,必须在对象初始化的时候,给 const 元素初始化。此时就用到了"初始化列表"。
"析构函数:与构造函数的调用顺序相反。"
12.深拷贝和浅拷贝
class Person
{
public:
Person(int a, int b) :_b(b),_a(b){} "初始化列表"
"深拷贝"
Person(const Person &p1)
{
mstr=(char *)malloc(strlen(p1.mstr) + 1); "核心步骤" “会进行内存拷贝”
strcpy(mstr, p1.mstr);
_a=p1._a;
_b=p1._b;
}
"浅拷贝"
Person(const Person &p1)
{
mstr=p1.mstr; "这里只会把一个对象的指针地址,赋给另一个对象的指针。";“不会进行内存拷贝”
_a=p1._a;
_b=p1._b;
}
void print_func()
{
cout << _a <<" " << _b << endl;
}
private:
int _a;
int _b;
char *mstr;
};
13.C++中 string 是一个类。
14.已知一个Print类,在该类的main()函数里执行语句 "Print a, b[2], *c;" 后;程序会自动调用该类的构造函数 4 次。
a会调用一次;b会调用两次;*c是空指针,没有开辟空间不会调用。