0

New to Cpp. In the given code, I don't know what change_val(int k) = 0; means and why the compiler prints

    error: cannot declare variable 'ob1' to be of abstract type 'B'
      B ob1(10);

    error: invalid new-expression of abstract class type 'B'
     ob2 = new B(100);

To my knowledge, neither B nor A has been declared abstract. Then why can't I declare a variable of B object? And is it possible to assign object of one class to another object type as in A *ob2; ob2 = new B(100);?


The code:

#include<iostream>
using namespace std;
class A{
    int x;
public:
    A(){x=0;}
    explicit A(int x){ cout<<"Constructor of A called"<<endl;
        this->x = x+100;}
    virtual void change_val(int k) = 0;
    void set_x(int k){x = k;}
    int get_val() const{ return x; }
    virtual void print_value()
    {
        cout<<x<<endl;
    }
};

class B : public A{
    public:
            explicit B(int k):A(k){
                cout<<"Constructor of B called"<<endl;
            }
    void print_value() override
        {
                cout<< get_val()+200<<endl;
        }
};

int main(){
    B ob1(10);
    ob1.print_value ();
    A *ob2;
    ob2 = new B(100);
    ob2->print_value ();
    ob2->change_val(20);
    ob2->print_value ();
}
Orni Sen
  • 5
  • 3
  • 3
    `virtual void change_val(int k) = 0;` declares a purely virtual function which needs to be overriden in a derived class. And since `class B` doesn't do that you cannot create an instance of it – UnholySheep Dec 07 '21 at 19:10
  • 1
    `To my knowledge, neither B nor A has been declared abstract.` A has by the presence of a pure virtual function, and B has as well by not providing an implementatio either. – Borgleader Dec 07 '21 at 19:11
  • 1
    Get a [good book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) and code along with it. This is code is currently more advanced than you are if you are unable to identify the abstract class and know how to rectify the situation. – sweenish Dec 07 '21 at 19:14
  • My comment literally answers that question?! I don't know why you are repeating your question – UnholySheep Dec 07 '21 at 19:17
  • @UnholySheep What i meant was how is it assigning a value to a function? I am new to cpp, you see. – Orni Sen Dec 07 '21 at 19:23
  • 1
    It is not assigning a value. It is declaring the function pure virtual, which is how you make a class abstract. – Stephen M. Webb Dec 07 '21 at 19:26

2 Answers2

1

We'll start with a quick review of your code:

#include<iostream>
using namespace std;  // Bad practice

class A{
    int x;
public:
    A(){x=0;}  // Bad practice; utilize default member initialization
    explicit A(int x){ cout<<"Constructor of A called"<<endl;  // Bad practice; utilize initialization section
        this->x = x+100;}  // Poor style throughout this code
    // Missing virtual destructor
    virtual void change_val(int k) = 0;  // Pure virtual function, makes A an abstract class
    void set_x(int k){x = k;}
    int get_val() const{ return x; }
    virtual void print_value()
    {
        cout<<x<<endl;  // Prefer '\n' over std::endl
    }
};

class B : public A{
    public:
            explicit B(int k):A(k){  // Why use initialization here but not above?
                cout<<"Constructor of B called"<<endl;
            }
    void print_value() override
        {
                cout<< get_val()+200<<endl;
        }

    // Failed to override pure virtual function change_val(), making B abstract as well
};

int main(){
    B ob1(10);
    ob1.print_value ();
    A *ob2;
    ob2 = new B(100);  // Initialize on same line as declaration
    ob2->print_value ();
    ob2->change_val(20);
    ob2->print_value ();
    // Missing delete
}

This is subjective, so I didn't note it above, but it's also possible for the member x to be protected in A so that B can directly access it instead of going through a getter. My code below does this. I see no reason to make implementing children classes more difficult.

#include <iostream>
// using namespace std;  // Bad practice

class A {
 protected:
  int x = 0;

 public:
  A() = default;
  virtual ~A() = default;
  explicit A(int x) : x(x + 100) { std::cout << "A ctor" << '\n'; }

  // Pure virtual function, makes A abstract
  virtual void change_val(int k) = 0;
  void set_x(int k) { x = k; }
  int get_val() const { return x; }
  virtual void print_value() { std::cout << x << '\n'; }
};

class B : public A {
 public:
  explicit B(int k) : A(k) { std::cout << "B ctor" << '\n'; }

  // Simpler function due to x being protected now in A
  void print_value() override { std::cout << x + 200 << '\n'; }

  // Overriding the pure virtual function makes B a concrete class now
  void change_val(int k) override { x = k; }
};

int main() {
  B ob1(10);
  ob1.print_value();
  A *ob2 = new B(100);
  ob2->print_value();
  ob2->change_val(20);
  ob2->print_value();

  delete ob2;
}

Output:

❯ ./a.out 
A ctor
B ctor
310
A ctor
B ctor
400
220

I noted that a virtual destructor was missing. It's important to have this for all base classes so that your derived objects can be properly destroyed.

You also called new without a subsequent delete. In bigger programs doing actual work, this would have leaked memory. To avoid this issue, you could use std::unique_ptr from <memory>. It handles the creation and destruction for you.

#include <memory>

/*
 * Your code
 */

int main() {
  B ob1(10);
  ob1.print_value();
  std::unique_ptr<A> ob2(new B(100));
  ob2->print_value();
  ob2->change_val(20);
  ob2->print_value();
}
sweenish
  • 4,793
  • 3
  • 12
  • 23
1

To my knowledge, neither B nor A has been declared abstract.

In c++ there is no abstract keyword (like in Java, for example) or declaration. The compiler decides for you whether the class is abstract using its definition. In c++ the class is abstract, if it contains one or more pure virtual methods (that = 0; thing after method signature).

So, A is implicitly declared as abstract (it has pure virtual method change_val()). B extends A and overrides the print_value() method. However, it does not implement the change_val() method, so it is implicitly declared as abstract too.

So it is easy to understand the errors your compiler generated.

    error: cannot declare variable 'ob1' to be of abstract type 'B'
      B ob1(10);

That happens because you try to instantiate an instance of abstract class B. It is impossible in other languages too.

The explanation for the second error is the same.

    error: invalid new-expression of abstract class type 'B'
     ob2 = new B(100);

You are trying to instantiate B which is an abstract type (sometimes called incomplete type).

And is it possible to assign object of one class to another object type as in A *ob2; ob2 = new B(100);?

If you create class C which inherits B and implements the change_val() method, so the class is not abstract anymore, it is possible to say for example:

class A{
    int x;
public:
    A(){
        x=0;
    }

    explicit A(int x) {
        cout << "Constructor of A called" << endl;
        this->x = x + 100;
    }

    virtual void change_val(int k) = 0;

    void set_x(int k) {
        x = k;
    }

    int get_val() const {
        return x; 
    }

    virtual void print_value() {
        cout << x << endl;
    }
};

class B : public A {
public:
    explicit B(int k): A(k) {
        cout << "Constructor of B called" << endl;
    }

    void print_value() override {
        cout << get_val() + 200 << endl;
    }
};

class C : public B {
public:
    explicit C(int number) : B(number) {
        cout << "Constructor of C called" << endl;
    }

    void change_val(int number) override {
        set_x(number);
    }
};

int main() {
    A* obj = new C(100);

    obj->change_val(200);
    obj->print_value();
}

The following program will output 400.

Sunfline
  • 139
  • 2
  • 8