主要是存放CPU执行的机器指令,特点是共享,只读;
包含常量区;主要是存放全局变量,静态变量,字符串常量,const修饰的全局变量, 不包括const修饰的局部变量(区域的数据在程序结束后由操作系统释放)
栈区的数据由编译器负责管理开辟和释放;(不要返回局部变量的地址);;局部变量,函数形参,栈区的数据在函数执行完后自动释放;
由程序员分配释放,若程序员不释放,操作系统会回收;在C++主要使用new申请空间;new ===>delete,,,,new[]====>delete[]
注意::对于char p[10] = "hello!!!\n"
可以对p进行修改,因为p是字符数组,存放在栈区,所以可以进行修改,但是如果是这样的char *p = "hello!!!\n"
那么就不能进行修改,因为p是一个指向常量区的指针,所以p指针指向的内容是不能变的;如果这样写:char *p = new char[10]; strcpy(p, "hello!!!\n");p[2] = d;
这样也不会发生错误,因为在堆上申请了空间;
在定义引用要初始化,不然就是不合法,因为引用就是为一个变量或者常量定义别名;int a = 10; int &b = a;
在这里b就是a的别名,操作b就是相当于操作a; const int &c = 10;
就是定义引用之后,不能在对引用变量进行取地址并且赋值操作:如以下就是不合法:int a = 10; int &b = a; int c = 233; &b = c
3、做函数参数,可以达到指针效果
int func(int a, int b = 100, int c = 100)
之后调用的时候可以使用以下调用方式:func(10), func(100, 100), func(100, 1000, 1000)
在使用函数参数的时候需要注意以下两个点:函数声明和定义不能同时用默认参数,最多只能有一个有;在使用默认参数时,第一个默认参数后面必须都是默认参数,才是合法如int func(int a = 10, int b, int c)
是不合法的!!!
有变量类型,但是无变量名:::int func(int a, int)
占位参数也可以是默认参数:::int func(int a, int = 10)
同一个作用域下, 函数名称相同,函数参数,类型,个数,顺序不同;;;;函数返回值不能作为重载条件;
int func(const int & a);与int func(int& a)也是重载
当实参是变量,优先调用后者,反之调用前者
int func(int a, int b = 10); int func(int a)
当实参是一个参数会产生二义性,导致错误,,如果两个实参就不会报错
class里默认数据权限是private, struct默认数据权限是public
析构函数无参数,不可发生重载, 构造函数可以有参数,,允许重载;
按参数分:有参构造和无参构造
按类型分:普通构造和拷贝构造
调用方式:括号法(Person p()), 显示法, 隐式转换法(可以看之前的一片文章关于转换函数的);
使用已经创建完毕的对象来初始化一个新对象:Person p1(10); Person p2(p1);
值传递的方式给函数参数传值:void doWork(Person p){} int main(){Person p1, doWork(p1); //会调用拷贝构造函数},为什么呢?因为值传递的话,,会复制一份p1到doWork的形参中,而不是原来的p1,所以相当于是用p1拷贝构造了一个变量
值传递返回局部对象: Person doWork(){Person p1; return p1 //这里也会调用拷贝构造},意思跟值传递一样的意思
默认情况下:默认构造函数, 默认析构函数, 拷贝构造函数都会默认提供;
如果用户自己定义了有参构造函数, 那么编译器不会提供默认构造函数;但是会提供拷贝构造函数;
如果用户自己定义了拷贝构造函数, 那么编译器其他的构造函数都不会提供;
在进行拷贝构造函数的时候,相当于是把一个对象的所有内容复制到了另一个对象之中,但是如果在原来的对象里存在堆区的数据,也就是程序员自己申请了堆区的空间,就会产生问题:因为在执行完之后,要手动释放堆区空间,,现在有两个对象指向同一片堆空间,如果其中一个释放了堆空间,另一个再来释放就会报错,这种错误就是浅拷贝导致的;为了解决这种问题,就要引入深拷贝,也就是在拷贝构造的时候,先把堆空间释放,,再自己申请一个新的堆区,这样就可以不用两个对象共用一个堆空间;
浅拷贝:简单的赋值操作;就会导致堆区的空间的重复释放;;
深拷贝:(如果有堆区空间)在拷贝赋值里面自己另外申请一个空间;;Person(const Person& p){m_height = new int(*p.m_height);};
构造函数():属性1(值1), 属性2(值2), 属性3(值3)...{};
构造函数及其析构函数的顺序:构造函数::由里到外,,,, 析构函数::由外到里
静态成员变量:所有对象共享同一个数据, 在编译阶段分配内存,类内声明static int num;
,类外初始化:int Person::num = 0
静态成员函数:所有对象共享同一个函数, 静态成员函数只能访问静态变量;
通过对象访问:Person p; p.func();
通过类名: Person::func()
Person p; 空对象占用的内存空间为1;C++编译器会给每个空对象也分配一个字节, 是为了区分空对象占内存的位置; 每个空对象都有个独一无二的内存地址;
解决名称冲突:Person(int age){this->age = age;}
返回对象本身用*this:
Person& addAge(Person p){
this->age += p.age;
return *this;
}
Person p1(10);
Person p2(0);
p2.addAge(p1).addAge(p1);===>p2.age = 30;
如果addAge函数的返回值把引用去掉,,那就是会调用一个新对象的拷贝构造,,,从而不再是p2了,最后结果就是20;
Person p = NULL; 如果没有涉及到访问非静态成员,那就是合法的;;;
成员函数后加const后我们称为常函数;
常函数内不可以修改成员属性;
成员属性声明前加关键字mutable后,在常函数中依然可以修改;
声明对象前加const称该对象为常对象,常对象只能调用常函数;
在类内任意位置:friend void func(const Person& p);
在全局区域:void func(const Person& p){})
在类内任意位置:friend void GoodGay::func(const Person& p);
在类外:void GoodGay::func(const Person& p){}
在类内的任意位置:friend class GoodGay;
在类外:class GoodGay{...}
#ifndef __MYCLASS_H_
#define __MYCLASS_H_
#include<iostream>
#include<string>
using namespace std;
class Complex{
public:
Complex(int _m_rel = 0, int _m_vir = 0) :m_rel(_m_rel), m_vir(_m_vir){}
//成员函数重载
Complex& operator +=(const Complex& c1);
Complex& operator -=(const Complex& c1);
Complex& operator++(); //前置++ ++Complex,返回引用
Complex operator++(int); //后置++,返回值
//赋值运算符的重载,如果涉及到堆区内存,,要先释放原来的堆区空间,,再来分配新赋值的空间,防止浅拷贝
//函数调用运算符重载;调用的时候直接类名(字符串),或者类对象(字符串)
void operator()(string test){
cout << test << endl;
}
int getRel()const{return m_rel;}
int getVir()const{return m_vir;}
void test_print();
private:
int m_rel, m_vir;
friend Complex operator +(const Complex& c1, const Complex& c2);
friend Complex operator -(const Complex& c1, const Complex& c2);
friend Complex operator *(const Complex& c1, const Complex& c2);
friend Complex operator /(const Complex& c1, const Complex& c2);
friend bool operator == (const Complex& c1, const Complex& c2);
friend bool operator != (const Complex& c1, const Complex& c2);
friend ostream& operator <<(ostream& os, const Complex& c1);
friend istream& operator >>(istream& is, const Complex& c1);
};
inline Complex operator +(const Complex& c1, const Complex& c2){
return Complex(c1.m_rel + c2.m_rel, c1.m_vir + c2.m_vir);
}
inline Complex operator -(const Complex& c1, const Complex& c2){
return Complex(c1.m_rel - c2.m_rel, c1.m_vir - c2.m_vir);
}
inline Complex operator *(const Complex& c1, const Complex& c2){
return Complex(c1.m_rel * c2.m_rel, c1.m_vir * c2.m_vir);
}
inline Complex operator /(const Complex& c1, const Complex& c2){
return Complex(c1.m_rel / c2.m_rel, c1.m_vir / c2.m_vir);
}
inline bool operator == (const Complex& c1, const Complex& c2){
if (c1.m_rel == c2.m_rel && c1.m_vir == c2.m_vir){
return true;
}
return false;
}
inline bool operator != (const Complex& c1, const Complex& c2){
if (c1.m_rel != c2.m_rel || c1.m_vir != c2.m_vir){
return true;
}
return false;
}
inline Complex& Complex::operator += (const Complex& c1){
this->m_rel += c1.m_rel;
this->m_vir += c1.m_vir;
return *this;
}
inline Complex& Complex::operator -= (const Complex& c1){
this->m_rel -= c1.m_rel;
this->m_vir -= c1.m_vir;
return *this;
}
inline ostream& operator <<(ostream& os, const Complex& c1){
return os << "[" << c1.m_rel << "," << c1.m_vir << "]" << endl;
}
inline istream& operator >> (istream& is, const Complex& c1){
return is >> c1.m_rel >> c1.m_vir;
}
inline Complex& Complex::operator ++(){
++this->m_rel;
++this->m_vir;
return *this;
}
inline Complex Complex::operator ++(int){
Complex old = *this;
this->m_rel ++;
this->m_vir ++;
return old;
}
#endif
公共继承(class B : public A):除了private不能子类不能访问, 其他类型子类都可以访问父类,外界只可以访问public类型;
保护继承(class B : protected A):除了private不能访问,其他都能, 但是外界一个数据都不能访问B;
私有继承(class B : private A):除了private不能访问,其他都能, 但是外界一个数据都不能访问B;
在父类中,所有的非静态成员变量(包括private)都要继承到子类(不论权限);只是在子类访问的时候,编译器会把private类型的数据隐藏起来
先构造父亲,再构造儿子;;先析构儿子,再构造父亲
同名成员变量:访问子类中的,自己点后面接变量名就行Son s; s.m_A, 访问父类中的需要加作用域; Son s; s.Base::m_A,;
同名成员函数:(在没有重载的时候),跟同名成员变量一样;如果子类中出现了父类的成员函数同名的成员函数,子类会自动隐藏父类所有的同名函数,要想访问就要加作用域;;;;
同名静态成员:处理方式与同名非静态成员一致;通过类名:子类:Son::m_A,, 父类:Son::Base::m_A;
多继承可能会引发多个同名成员,需要加作用域才能访问;class A: public B1, public B2;
问题:(1)羊继承了动物数据, 驼也继承了动物数据,当羊驼使用动物数据,会产生二义性;(加作用域)
(2)羊驼继承了两份动物数据,造成重复情况:造成了资源浪费(利用虚继承,在继承之前加入virtual)
class Sheep : virtual public animal{}., class Tou : virtual public animal{};vbptr(虚基类指针):指向虚基类表
#include<iostream>
using namespace std;
class Animal{
public:
void doSpeak(){
cout << "动物在说话" << endl;
}
};
class Cat : public Animal{
public:
void doSpeak(){
cout << "小猫在说话" << endl;
}
};
class Dog : public Animal{
public:
void doSpeak(){
cout << "小狗在说话" << endl;
}
};
void doSpeak(Animal & animal){
animal.doSpeak();
}
void test1(){
cout << "sizeof(Animal) = " << sizeof(Animal) << endl;
int main(){
Cat cat;
Dog dog;
doSpeak(cat);
doSpeak(dog);
test1();
return 0;
}
//以上代码的输出程序很明显:因为这是静态动态,地址在编译阶段就已经确定,所以使用animal的类成员函数,
//调用的肯定都是animal类里的doSpeak函数,所以输出两个动物在说话;;而且此时animal类的大小为1;
//如果在animal类里面的doSpeak的返回值前加入一个virtual,那就成了动态多态:
//虽然使用的还是animal.doSpeak(),但是地址是在运行时绑定的,所以这个绑定的地址其实就是实参这里new的空间地址,
//所以就是new什么输出什么,所以输出一个小猫在说话,一个小狗在说话
//此时animal类的大小是4;虽然Animal类里无非静态成员变量,但是加了virtual后,,就会多一个指针
// (vfptr:虚函数指针)
//指向vftable(记录虚函数地址)
纯虚函数:virtual 返回值 函数名(参数列表) = 0;
只要有一个纯虚函数的类,被称为抽象类;
抽象类特点:无法实例化对象, 抽象类的子类必须要重写父类中的纯虚函数,否则子类也为抽象类;
多态使用时, 如果子类中有属性开辟到堆区, 那么父类指针在释放时无法调用到子类的析构代码;(即:子类的析构函数不会被调用)
上面问题解决:将父类中的析构函数改为虚析构或者纯虚析构即可;(纯虚析构函数也要实现,在外界实现)
#include<iostream>
using namespace std;
class AbstaractDrink{
public:
virtual void Boil() = 0; //煮水
virtual void Brew() = 0; //冲泡
virtual void PourInCup() = 0; //倒入杯中
virtual void PutSomeThing() = 0;//加入辅料
void makeDrink(){
Boil();
Brew();
PourInCup();
PutSomeThing();
}
};
class Coffee : public AbstaractDrink{
public:
void Boil(){
cout << "煮农夫山泉" << endl;
}
void Brew(){
cout << "冲泡咖啡" << endl;
}
void PourInCup(){
cout << "倒入咖啡杯中" << endl;
}
void PutSomeThing(){
cout << "加入牛奶 " << endl;
}
};
class Tea : public AbstaractDrink{
public:
void Boil(){
cout << "煮百岁山" << endl;
}
void Brew(){
cout << "冲泡茶叶" << endl;
}
void PourInCup(){
cout << "倒入茶杯中" << endl;
}
void PutSomeThing(){
cout << "加入枸杞 " << endl;
}
};
void doWork(AbstaractDrink *abs){
abs->makeDrink();
delete abs;
}
int main(){
cout << "-----制作咖啡------" << endl;
doWork(new Coffee);
cout << "-----制作茶叶------" << endl;
doWork(new Tea);
return 0;
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。